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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +2 -0
- data/Rakefile +10 -0
- data/ext/yarv_generator/extconf.rb +67 -0
- data/ext/yarv_generator/yarv_generator.c +400 -0
- data/lib/yarv_generator/instruction.rb +46 -0
- data/lib/yarv_generator/iseq.rb +55 -0
- data/lib/yarv_generator/version.rb +3 -0
- data/lib/yarv_generator.rb +17 -0
- data/yarv_generator.gemspec +28 -0
- metadata +119 -0
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
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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
data/Rakefile
ADDED
@@ -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,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: []
|