yarv_generator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []