yarv_generator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e640b6998d4da9108ebf2e1d2fbdaa95c5e6b4be
4
+ data.tar.gz: 8b4e6416f02db3d9205c12f782dc54df6d2cf898
5
+ SHA512:
6
+ metadata.gz: 278b941a61d9b4b4daf1914da8e869b9e2b4e7d1a1491fac84f445a0490d847285716826544e555d5069401c2057b5206e25bcc1f9e0c90a142112d0a87b6f25
7
+ data.tar.gz: 937e8ec528e237004138daf9968cdd615c0dfbef1227eeba76024bca78ee525197ef23d7b5e66282d92b5384c836dd9b7f999e7e2d7ec59255a1d64d08448db0
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ tags
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.16.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ yarv_generator (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ rake (10.5.0)
11
+ rake-compiler (1.0.4)
12
+ rake
13
+ rspec (3.7.0)
14
+ rspec-core (~> 3.7.0)
15
+ rspec-expectations (~> 3.7.0)
16
+ rspec-mocks (~> 3.7.0)
17
+ rspec-core (3.7.0)
18
+ rspec-support (~> 3.7.0)
19
+ rspec-expectations (3.7.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.7.0)
22
+ rspec-mocks (3.7.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.7.0)
25
+ rspec-support (3.7.0)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bundler (~> 1.16)
32
+ rake (~> 10.0)
33
+ rake-compiler
34
+ rspec (~> 3.0)
35
+ yarv_generator!
36
+
37
+ BUNDLED WITH
38
+ 1.16.0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Nguyễn Quang Minh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Nguyễn Quang Minh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # yarv-generator
2
+ Generate ruby-friendly YARV instructions
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rake/extensiontask'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task default: :spec
7
+
8
+ Rake::ExtensionTask.new 'yarv_generator' do |ext|
9
+ ext.lib_dir = 'lib/yarv_generator'
10
+ end
@@ -0,0 +1,67 @@
1
+ require 'mkmf'
2
+ require 'fileutils'
3
+
4
+ make_dir = FileUtils.pwd
5
+
6
+ # Creating tmp dir
7
+ working_dir = File.expand_path('../../tmp', File.dirname(__FILE__))
8
+ FileUtils.mkdir_p(working_dir)
9
+ puts "Working dir: #{working_dir}"
10
+
11
+ # Clone Ruby source code
12
+ ruby_source = File.expand_path('ruby', working_dir)
13
+ if File.exist?(ruby_source)
14
+ puts "Ruby source code exists at '#{ruby_source}'."
15
+ else
16
+ puts "Ruby source code not exist. Cloning at '#{ruby_source}'."
17
+ result = system("git clone git@github.com:ruby/ruby #{ruby_source}")
18
+ unless result
19
+ puts 'Fail to clone ruby source code. Aborting.'
20
+ exit
21
+ end
22
+ end
23
+
24
+ # Checkout into current ruby version
25
+ FileUtils.cd(ruby_source)
26
+ ruby_version = "v#{RUBY_VERSION.gsub(/\./i, '_')}"
27
+ puts "Checking out into Ruby version #{RUBY_VERSION} with tag #{ruby_version}."
28
+ result = system("git checkout #{ruby_version}")
29
+ unless result
30
+ puts "Fail to check out into Ruby version #{RUBY_VERSION}.
31
+ Only official releases are supported. Aborting."
32
+ exit
33
+ end
34
+
35
+ # Generate configure file
36
+ puts 'Generate configure file ...'
37
+ result = system('autoconf')
38
+ unless result
39
+ puts 'Fail to generate configure file.
40
+ Please sure autoconf is installed. Aborting'
41
+ exit
42
+ end
43
+
44
+ # Run configure to setup compile-ready configurations
45
+ puts 'Runing configure ...'
46
+ result = system('./configure')
47
+ unless result
48
+ puts 'Fail to compile ruby source code. Aborting.'
49
+ exit
50
+ end
51
+
52
+ # Compile ruby source code
53
+ puts 'Compiling Ruby ...'
54
+ result = system('make')
55
+ unless result
56
+ puts 'Fail to compile ruby source code. Aborting.'
57
+ exit
58
+ end
59
+
60
+ # Go back and continue compiling gem
61
+ FileUtils.cd(make_dir)
62
+
63
+ cflags = " -I #{ruby_source} -I #{ruby_source}/include -I #{ruby_source}/.ext/include/#{RUBY_PLATFORM}"
64
+
65
+ with_cflags(RbConfig::CONFIG['CFLAGS'] + cflags) do
66
+ create_makefile 'yarv_generator/yarv_generator'
67
+ end
@@ -0,0 +1,400 @@
1
+ #include "ruby/ruby.h"
2
+ #include "ruby/st.h"
3
+ #include "vm_core.h"
4
+ #include "eval_intern.h"
5
+ #include "ruby/encoding.h"
6
+ #include "internal.h"
7
+ #include "iseq.h"
8
+ #include "node.h"
9
+ #include "insns.inc"
10
+ #include "insns_info.inc"
11
+
12
+ VALUE yarv_builder_build_from_source(VALUE self, VALUE src);
13
+ VALUE yarv_builder_build_yarv_tree(rb_iseq_t *iseq);
14
+ VALUE yarv_builder_insn_type(rb_iseq_t *iseq);
15
+ VALUE yarv_builder_local_table(rb_iseq_t *iseq);
16
+ VALUE yarv_builder_instructions(rb_iseq_t *iseq, st_table *labels_table);
17
+ VALUE yarv_builder_params(rb_iseq_t *iseq, st_table *labels_table);
18
+ VALUE yarv_builder_catch_table(rb_iseq_t *iseq, st_table *labels_table);
19
+ VALUE yarv_builder_call_info(VALUE *seq);
20
+ VALUE obj_resurrect(VALUE obj);
21
+ VALUE register_label(struct st_table *table, unsigned long idx);
22
+ int cdhash_each(VALUE key, VALUE value, VALUE ary);
23
+
24
+ VALUE
25
+ yarv_builder_build_from_source(VALUE self, VALUE src)
26
+ {
27
+ rb_iseq_t *iseq = rb_iseq_compile_with_option(src, rb_str_new2("<compiled>"), Qnil, INT2FIX(1), 0, Qnil);
28
+ return yarv_builder_build_yarv_tree(iseq);
29
+ }
30
+
31
+ VALUE
32
+ yarv_builder_build_yarv_tree(rb_iseq_t *iseq)
33
+ {
34
+ VALUE rb_cYarvIseq = rb_path2class("YarvGenerator::Iseq");
35
+ VALUE iseq_object = rb_funcall(rb_cYarvIseq, rb_intern("new"), 0);
36
+
37
+ struct st_table *labels_table = st_init_numtable();
38
+
39
+ VALUE type = yarv_builder_insn_type(iseq);
40
+ rb_funcall(iseq_object, rb_intern("type="), 1, type);
41
+
42
+ VALUE local_table = yarv_builder_local_table(iseq);
43
+ rb_funcall(iseq_object, rb_intern("local_table="), 1, local_table);
44
+
45
+ VALUE params = yarv_builder_params(iseq, labels_table);
46
+ rb_funcall(iseq_object, rb_intern("params="), 1, params);
47
+
48
+ VALUE catch_table = yarv_builder_catch_table(iseq, labels_table);
49
+ rb_funcall(iseq_object, rb_intern("catch_table="), 1, catch_table);
50
+
51
+ VALUE instructions = yarv_builder_instructions(iseq, labels_table);
52
+ rb_funcall(iseq_object, rb_intern("instructions="), 1, instructions);
53
+
54
+ rb_funcall(iseq_object, rb_intern("label="), 1, iseq->body->location.label);
55
+ rb_funcall(iseq_object, rb_intern("path="), 1, iseq->body->location.path);
56
+ rb_funcall(iseq_object, rb_intern("absolute_path="), 1, iseq->body->location.absolute_path);
57
+ rb_funcall(iseq_object, rb_intern("first_lineno="), 1, iseq->body->location.first_lineno);
58
+ rb_funcall(iseq_object, rb_intern("arg_size="), 1, INT2FIX(iseq->body->param.size));
59
+ rb_funcall(iseq_object, rb_intern("local_size="), 1, INT2FIX(iseq->body->local_size));
60
+ rb_funcall(iseq_object, rb_intern("stack_max="), 1, INT2FIX(iseq->body->stack_max));
61
+
62
+ st_free_table(labels_table);
63
+
64
+ return iseq_object;
65
+ }
66
+
67
+
68
+ VALUE
69
+ yarv_builder_insn_type(rb_iseq_t *iseq)
70
+ {
71
+ switch(iseq->body->type) {
72
+ case ISEQ_TYPE_TOP:
73
+ return ID2SYM(rb_intern("top"));
74
+ break;
75
+ case ISEQ_TYPE_MAIN:
76
+ return ID2SYM(rb_intern("main"));
77
+ break;
78
+ case ISEQ_TYPE_EVAL:
79
+ return ID2SYM(rb_intern("eval"));
80
+ break;
81
+ case ISEQ_TYPE_METHOD:
82
+ return ID2SYM(rb_intern("method"));
83
+ break;
84
+ case ISEQ_TYPE_BLOCK:
85
+ return ID2SYM(rb_intern("block"));
86
+ break;
87
+ case ISEQ_TYPE_CLASS:
88
+ return ID2SYM(rb_intern("class"));
89
+ break;
90
+ case ISEQ_TYPE_RESCUE:
91
+ return ID2SYM(rb_intern("rescue"));
92
+ break;
93
+ case ISEQ_TYPE_ENSURE:
94
+ return ID2SYM(rb_intern("ensure"));
95
+ break;
96
+ case ISEQ_TYPE_DEFINED_GUARD:
97
+ return ID2SYM(rb_intern("defined_guard"));
98
+ break;
99
+ default: rb_bug("iseq type %d not found", iseq->body->type);
100
+ };
101
+ }
102
+
103
+ VALUE
104
+ yarv_builder_local_table(rb_iseq_t *iseq)
105
+ {
106
+ VALUE local_table = rb_ary_new();
107
+ for (unsigned int i = 0; i < iseq->body->local_table_size; i++) {
108
+ ID var_id = iseq->body->local_table[i];
109
+ if (var_id) {
110
+ if (rb_id2str(var_id)) {
111
+ rb_ary_push(local_table, ID2SYM(var_id));
112
+ }
113
+ else { /* Fix weird ID problem. This fix comes from iseq.c */
114
+ rb_ary_push(local_table, ULONG2NUM(iseq->body->local_table_size-i+1));
115
+ }
116
+ } else {
117
+ rb_ary_push(local_table, ID2SYM(rb_intern("arg_rest")));
118
+ }
119
+ }
120
+ return local_table;
121
+ }
122
+
123
+
124
+ VALUE
125
+ yarv_builder_instructions(rb_iseq_t *iseq, st_table *labels_table)
126
+ {
127
+ VALUE instructions = rb_ary_new();
128
+ VALUE rb_cYarvIBuilder = rb_path2class("YarvGenerator::InstructionBuilder");
129
+ VALUE instruction_builder = rb_funcall(rb_cYarvIBuilder, rb_intern("new"), 0);
130
+
131
+ VALUE *iseq_original = rb_iseq_original_iseq((rb_iseq_t *)iseq);
132
+
133
+ for (VALUE *seq = iseq_original; seq < iseq_original + iseq->body->iseq_size; ) {
134
+ VALUE insn = *seq++;
135
+ int len = insn_len_info[(int)insn];
136
+ VALUE operands = rb_ary_new2(len);
137
+
138
+ VALUE *nseq = seq + len;
139
+
140
+ for (int j = 0; j < len - 1; j++, seq++) {
141
+ VALUE operand = Qnil;
142
+ switch (insn_op_type(insn, j)) {
143
+ case TS_LINDEX:
144
+ case TS_NUM:
145
+ operand = INT2FIX(*seq);
146
+ break;
147
+ case TS_VALUE:
148
+ operand = obj_resurrect(*seq);
149
+ break;
150
+ case TS_CALLCACHE:
151
+ operand = Qfalse;
152
+ break;
153
+ case TS_ID:
154
+ operand = ID2SYM(*seq);
155
+ break;
156
+ case TS_FUNCPTR:
157
+ #if SIZEOF_VALUE <= SIZEOF_LONG
158
+ operand = LONG2NUM((SIGNED_VALUE)*seq);
159
+ #else
160
+ operand = LL2NUM((SIGNED_VALUE)*seq);
161
+ #endif
162
+ break;
163
+ case TS_GENTRY:
164
+ {
165
+ struct rb_global_entry *entry = (struct rb_global_entry *)*seq;
166
+ operand = ID2SYM(entry->id);
167
+ }
168
+ break;
169
+ case TS_OFFSET:
170
+ {
171
+ unsigned long idx = nseq - iseq_original + *seq;
172
+ operand = register_label(labels_table, idx);
173
+ }
174
+ break;
175
+ case TS_IC:
176
+ {
177
+ union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)*seq;
178
+ operand = INT2FIX(is - iseq->body->is_entries);
179
+ }
180
+ break;
181
+ case TS_ISEQ:
182
+ {
183
+ const rb_iseq_t *iseq = (rb_iseq_t *)*seq;
184
+ if (iseq) {
185
+ operand = yarv_builder_build_yarv_tree((rb_iseq_t *)rb_iseq_check(iseq));
186
+ }
187
+ else {
188
+ operand = Qnil;
189
+ }
190
+ }
191
+ break;
192
+ case TS_CALLINFO:
193
+ operand = yarv_builder_call_info(seq);
194
+ break;
195
+ case TS_CDHASH:
196
+ {
197
+ VALUE hash = *seq;
198
+ operand = rb_ary_new();
199
+ rb_hash_foreach(hash, cdhash_each, operand);
200
+
201
+ for (int i=0; i<RARRAY_LEN(operand); i+=2) {
202
+ VALUE pos = FIX2INT(rb_ary_entry(operand, i+1));
203
+ unsigned long idx = nseq - iseq_original + pos;
204
+
205
+ rb_ary_store(operand, i+1, register_label(labels_table, idx));
206
+ }
207
+ }
208
+ break;
209
+ default:
210
+ rb_bug("unknown operand: %c", insn_op_type(insn, j));
211
+ break;
212
+ }
213
+ rb_ary_push(operands, operand);
214
+ }
215
+
216
+ VALUE insn_object = rb_funcall(instruction_builder, rb_intern("build"), 2, rb_str_new2(insn_name(insn)), operands);
217
+ rb_ary_push(instructions, insn_object);
218
+ }
219
+
220
+ for (unsigned int i = 0, pos = 0, line_pos = 0; i < RARRAY_LEN(instructions); i++) {
221
+ VALUE instruction = RARRAY_AREF(instructions, i);
222
+
223
+ st_data_t label;
224
+ if (st_lookup(labels_table, pos, &label)) {
225
+ rb_funcall(instruction, rb_intern("label="), 1, label);
226
+ }
227
+
228
+ if (line_pos < iseq->body->line_info_size && iseq->body->line_info_table[line_pos].position == pos) {
229
+ rb_funcall(instruction, rb_intern("line_no="), 1, INT2FIX(iseq->body->line_info_table[line_pos].line_no));
230
+ line_pos++;
231
+ }
232
+ pos += RARRAY_LENINT(rb_funcall(instruction, rb_intern("operands"), 0)) + 1;
233
+ }
234
+
235
+ return instructions;
236
+ }
237
+
238
+ VALUE yarv_builder_params(rb_iseq_t *iseq, st_table *labels_table) {
239
+ VALUE params = rb_hash_new();
240
+
241
+ if (iseq->body->param.flags.has_lead) {
242
+ rb_hash_aset(params, ID2SYM(rb_intern("lead_num")), INT2FIX(iseq->body->param.lead_num));
243
+ }
244
+
245
+ if (iseq->body->param.flags.has_opt) {
246
+ VALUE opts = rb_ary_new2(iseq->body->param.opt_num);
247
+ for (int i = 0; i < iseq->body->param.opt_num; i++) {
248
+ VALUE value = iseq->body->param.opt_table[i];
249
+ register_label(labels_table, value);
250
+ rb_ary_push(opts, value);
251
+ }
252
+ rb_hash_aset(params, ID2SYM(rb_intern("opt")), opts);
253
+ }
254
+
255
+ if (iseq->body->param.flags.has_rest) {
256
+ rb_hash_aset(params, ID2SYM(rb_intern("rest_start")), INT2FIX(iseq->body->param.rest_start));
257
+ }
258
+
259
+ if (iseq->body->param.flags.has_post) {
260
+ rb_hash_aset(params, ID2SYM(rb_intern("post_start")), INT2FIX(iseq->body->param.post_start));
261
+ rb_hash_aset(params, ID2SYM(rb_intern("post_num")), INT2FIX(iseq->body->param.post_num));
262
+ }
263
+
264
+ if (iseq->body->param.flags.has_kw) {
265
+ VALUE keywords = rb_ary_new();
266
+ int j = 0;
267
+ for (int i = 0; i < iseq->body->param.keyword->num; i++) {
268
+ if (i < iseq->body->param.keyword->required_num) {
269
+ // Require params
270
+ rb_ary_push(keywords, ID2SYM(iseq->body->param.keyword->table[i]));
271
+ } else {
272
+ VALUE val = rb_ary_new2(2);
273
+ rb_ary_push(val, ID2SYM(iseq->body->param.keyword->table[i]));
274
+ rb_ary_push(val, iseq->body->param.keyword->default_values[j]);
275
+ rb_ary_push(keywords, val);
276
+ j++;
277
+ }
278
+ }
279
+ rb_hash_aset(params, ID2SYM(rb_intern("kwbits")),INT2FIX(iseq->body->param.keyword->bits_start));
280
+ rb_hash_aset(params, ID2SYM(rb_intern("keywords")), keywords);
281
+ }
282
+
283
+ if (iseq->body->param.flags.has_kwrest) {
284
+ rb_hash_aset(params, ID2SYM(rb_intern("kwrest")), INT2FIX(iseq->body->param.rest_start));
285
+ }
286
+
287
+ if (iseq->body->param.flags.has_block) {
288
+ rb_hash_aset(params, ID2SYM(rb_intern("block_start")), INT2FIX(iseq->body->param.block_start));
289
+ }
290
+
291
+ return params;
292
+ }
293
+
294
+ VALUE yarv_builder_catch_table(rb_iseq_t *iseq, st_table *labels_table) {
295
+ VALUE catch_table = rb_ary_new();
296
+ VALUE rb_cYarvCatchEntry = rb_path2class("YarvGenerator::CatchEntry");
297
+
298
+ if (iseq->body->catch_table) {
299
+ for (int i = 0; i < iseq->body->catch_table->size; i++) {
300
+ VALUE catch_entry = rb_funcall(rb_cYarvCatchEntry, rb_intern("new"), 0);
301
+
302
+ const struct iseq_catch_table_entry *entry = &iseq->body->catch_table->entries[i];
303
+ VALUE type;
304
+ switch (entry->type) {
305
+ case CATCH_TYPE_RESCUE:
306
+ type = ID2SYM(rb_intern("rescue")); break;
307
+ case CATCH_TYPE_ENSURE:
308
+ type = ID2SYM(rb_intern("ensure")); break;
309
+ case CATCH_TYPE_RETRY:
310
+ type = ID2SYM(rb_intern("retry")); break;
311
+ case CATCH_TYPE_BREAK:
312
+ type = ID2SYM(rb_intern("break")); break;
313
+ case CATCH_TYPE_REDO:
314
+ type = ID2SYM(rb_intern("redo")); break;
315
+ case CATCH_TYPE_NEXT:
316
+ type = ID2SYM(rb_intern("next")); break;
317
+ default:
318
+ rb_bug("unknown catch type %d", (int)entry->type);
319
+ }
320
+ rb_funcall(catch_entry, rb_intern("type="), 1, type);
321
+
322
+ if (entry->iseq) {
323
+ VALUE catch_iseq = yarv_builder_build_yarv_tree((rb_iseq_t *)rb_iseq_check(entry->iseq));
324
+ rb_funcall(catch_entry, rb_intern("iseq="), 1, catch_iseq);
325
+ }
326
+
327
+ rb_funcall(catch_entry, rb_intern("catch_start="), 1, register_label(labels_table, entry->start));
328
+ rb_funcall(catch_entry, rb_intern("catch_end="), 1, register_label(labels_table, entry->end));
329
+ rb_funcall(catch_entry, rb_intern("catch_continue="), 1, register_label(labels_table, entry->cont));
330
+ rb_funcall(catch_entry, rb_intern("sp="), 1, UINT2NUM(entry->sp));
331
+
332
+ rb_ary_push(catch_table, catch_entry);
333
+ }
334
+ }
335
+ return catch_table;
336
+ }
337
+
338
+ VALUE
339
+ yarv_builder_call_info(VALUE *seq)
340
+ {
341
+ VALUE rb_cYarvCallInfo = rb_path2class("YarvGenerator::CallInfo");
342
+ struct rb_call_info *ci = (struct rb_call_info *)*seq;
343
+
344
+ VALUE ci_object = rb_funcall(rb_cYarvCallInfo, rb_intern("new"), 0);
345
+ rb_funcall(ci_object, rb_intern("mid="), 1, ci->mid ? ID2SYM(ci->mid) : Qnil);
346
+ rb_funcall(ci_object, rb_intern("flag="), 1, UINT2NUM(ci->flag));
347
+ rb_funcall(ci_object, rb_intern("orig_argc="), 1, INT2FIX(ci->orig_argc));
348
+
349
+ if (ci->flag & VM_CALL_KWARG) {
350
+ struct rb_call_info_with_kwarg *ci_kw = (struct rb_call_info_with_kwarg *)ci;
351
+ VALUE kw = rb_ary_new2(ci_kw->kw_arg->keyword_len);
352
+ int len = ci->orig_argc - ci_kw->kw_arg->keyword_len;
353
+ for (int i = 0; i < len; i++) {
354
+ rb_ary_push(kw, ci_kw->kw_arg->keywords[i]);
355
+ }
356
+ rb_funcall(ci_object, rb_intern("kw_arg="), 1, kw);
357
+ }
358
+ return ci_object;
359
+
360
+ }
361
+
362
+ // Private method copied from iseq.c
363
+ inline VALUE
364
+ obj_resurrect(VALUE obj)
365
+ {
366
+ if (!SPECIAL_CONST_P(obj) && !RBASIC(obj)->klass) {
367
+ switch (BUILTIN_TYPE(obj)) {
368
+ case T_STRING:
369
+ obj = rb_str_resurrect(obj);
370
+ break;
371
+ case T_ARRAY:
372
+ obj = rb_ary_resurrect(obj);
373
+ break;
374
+ }
375
+ }
376
+ return obj;
377
+ }
378
+
379
+ VALUE
380
+ register_label(struct st_table *table, unsigned long idx)
381
+ {
382
+ VALUE sym = INT2FIX(idx);
383
+ st_insert(table, idx, sym);
384
+ return sym;
385
+ }
386
+
387
+ // Private method copied from iseq.c
388
+ int
389
+ cdhash_each(VALUE key, VALUE value, VALUE ary)
390
+ {
391
+ rb_ary_push(ary, obj_resurrect(key));
392
+ rb_ary_push(ary, value);
393
+ return ST_CONTINUE;
394
+ }
395
+
396
+ void Init_yarv_generator() {
397
+ VALUE rb_cYarvGenerator = rb_path2class("YarvGenerator");
398
+ VALUE rb_cYarvBuilder = rb_define_class_under(rb_cYarvGenerator, "Builder", rb_cObject);
399
+ rb_define_method(rb_cYarvBuilder, "build_from_source", &yarv_builder_build_from_source, 1);
400
+ }
@@ -0,0 +1,46 @@
1
+ class YarvGenerator
2
+ class InstructionBuilder
3
+ def build(name, operands)
4
+ Instruction.new(name, operands)
5
+ end
6
+ end
7
+
8
+ class Instruction
9
+ attr_accessor :name, :operands, :label, :line_no
10
+
11
+ def initialize(name, operands)
12
+ @name = name
13
+ @operands = operands
14
+ end
15
+
16
+ INSPECT_TEMPLATE = <<-INSPECT.strip
17
+ <%- unless label.nil? -%>
18
+ <Label <%= label -%>>
19
+ <%- end -%>
20
+ <%- if operands.any? { |o| o.is_a?(YarvGenerator::Iseq) } -%>
21
+ <%= name %>
22
+ <%- operands.each_with_index do |o, index|-%>
23
+ <%- if o.is_a?(YarvGenerator::Iseq) -%>
24
+ <%= YarvGenerator.indent(o.inspect, 1) %>
25
+ <%- else -%>
26
+ <%= YarvGenerator.indent(o.respond_to?(:inspect) ? o.inspect : o, 1) %>
27
+ <%- end -%>
28
+ <%- end -%>
29
+ <%- else -%>
30
+ <%= name%> <%= operands.map { |o| o.respond_to?(:inspect) ? o.inspect : o }.join(", ") %>
31
+ <%- end -%>
32
+ INSPECT
33
+
34
+ def inspect
35
+ ERB.new(INSPECT_TEMPLATE, nil, '-').result(binding)
36
+ end
37
+ end
38
+
39
+ class CallInfo
40
+ attr_accessor :mid, :flag, :orig_argc, :kw_arg
41
+
42
+ def inspect
43
+ "method(#{mid}, orig_argc: #{orig_argc})"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,55 @@
1
+ class YarvGenerator
2
+ class Iseq
3
+ attr_accessor :type, :local_table, :catch_table, :instructions, :params,
4
+ :label, :path, :absolute_path, :first_lineno, :stack_max,
5
+ :arg_size, :local_size
6
+
7
+ INSPECT_TEMPLATE = <<-INSPECT.strip
8
+ #<Iseq
9
+ type: <%= type %>, label: <%= label %>, path: <%= path %>:<%= first_lineno %>,
10
+ local_table: <%= local_table.inspect %>,
11
+ params: <%= params.inspect %>,
12
+ <%- if catch_table.empty? -%>
13
+ catch_table: <empty>
14
+ <%- else -%>
15
+ catch_table:
16
+ <%- catch_table.each do |entry| -%>
17
+ <%= YarvGenerator.indent(entry.inspect, 2) %>
18
+ <%- end -%>
19
+ <%- end -%>
20
+ <%- if instructions.empty? -%>
21
+ instructions: <empty>
22
+ <%- else -%>
23
+ instructions:
24
+ <%- instructions.each do |instruction| -%>
25
+ <%= YarvGenerator.indent(instruction.inspect, 2) %>
26
+ <%- end -%>
27
+ <%- end -%>
28
+ >
29
+ INSPECT
30
+
31
+ def inspect
32
+ ERB.new(INSPECT_TEMPLATE, nil, '-').result(binding)
33
+ end
34
+ end
35
+
36
+ class CatchEntry
37
+ attr_accessor :type, :iseq, :catch_start, :catch_end, :catch_continue, :sp
38
+
39
+ INSPECT_TEMPLATE = <<-INSPECT.strip
40
+ #<CatchEntry
41
+ type: <%= type %>, start: <%= catch_start %>, end: <%= catch_end %> continue: <%= catch_continue %>,
42
+ <%- if iseq.nil? -%>
43
+ iseq: <empty>
44
+ <%- else -%>
45
+ iseq:
46
+ <%= YarvGenerator.indent(iseq.inspect, 2) %>
47
+ <%- end -%>
48
+ >
49
+ INSPECT
50
+
51
+ def inspect
52
+ ERB.new(INSPECT_TEMPLATE, nil, '-').result(binding)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ class YarvGenerator
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'erb'
2
+ require 'yarv_generator/version'
3
+ class YarvGenerator
4
+ def self.build_from_source(src)
5
+ YarvGenerator::Builder.new.build_from_source(src)
6
+ end
7
+
8
+ def self.indent(str, span)
9
+ str.split("\n").map do |line|
10
+ "#{' ' * span}#{line}"
11
+ end.join("\n")
12
+ end
13
+ end
14
+ require 'yarv_generator/iseq'
15
+ require 'yarv_generator/instruction'
16
+ require 'yarv_generator/yarv_generator'
17
+
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'yarv_generator/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'yarv_generator'
7
+ spec.version = YarvGenerator::VERSION
8
+ spec.authors = ['Minh Nguyen']
9
+ spec.email = ['nguyenquangminh0711@gmail.com']
10
+
11
+ spec.summary = 'A ruby-friendly YARV instruction generator'
12
+ spec.description = 'Ruby YARV instructions are too "internal" and hard to access from outside. This gem was born aiming to provide a friendly way to access YARV instructions for Ruby developers'
13
+ spec.homepage = 'https://github.com/nguyenquangminh0711/yarv_generator'
14
+ spec.license = 'MIT'
15
+ spec.platform = Gem::Platform::RUBY
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(spec)/})
19
+ end
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.16'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+ spec.add_development_dependency 'rake-compiler'
26
+
27
+ spec.extensions = %w[ext/yarv_generator/extconf.rb]
28
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yarv_generator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Minh Nguyen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake-compiler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Ruby YARV instructions are too "internal" and hard to access from outside.
70
+ This gem was born aiming to provide a friendly way to access YARV instructions for
71
+ Ruby developers
72
+ email:
73
+ - nguyenquangminh0711@gmail.com
74
+ executables: []
75
+ extensions:
76
+ - ext/yarv_generator/extconf.rb
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - ".rspec"
81
+ - ".travis.yml"
82
+ - Gemfile
83
+ - Gemfile.lock
84
+ - LICENSE
85
+ - LICENSE.txt
86
+ - README.md
87
+ - Rakefile
88
+ - ext/yarv_generator/extconf.rb
89
+ - ext/yarv_generator/yarv_generator.c
90
+ - lib/yarv_generator.rb
91
+ - lib/yarv_generator/instruction.rb
92
+ - lib/yarv_generator/iseq.rb
93
+ - lib/yarv_generator/version.rb
94
+ - yarv_generator.gemspec
95
+ homepage: https://github.com/nguyenquangminh0711/yarv_generator
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.5.1
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: A ruby-friendly YARV instruction generator
119
+ test_files: []