neuroncheck 0.1.0 → 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.
- checksums.yaml +4 -4
- data/lib/neuroncheck/builtin_keyword.rb +55 -55
- data/lib/neuroncheck/cond_block.rb +30 -30
- data/lib/neuroncheck/declaration.rb +288 -288
- data/lib/neuroncheck/error.rb +6 -6
- data/lib/neuroncheck/kernel.rb +598 -598
- data/lib/neuroncheck/matcher.rb +229 -229
- data/lib/neuroncheck/plugin.rb +144 -144
- data/lib/neuroncheck/syntax.rb +44 -44
- data/lib/neuroncheck/utils.rb +80 -80
- data/lib/neuroncheck/version.rb +1 -1
- data/test/test_advanced_syntactical.rb +101 -101
- data/test/test_inheritance.rb +140 -140
- data/test/test_main.rb +863 -863
- data/test/test_main_syntax.rb +219 -219
- data/test/test_plugin.rb +77 -77
- metadata +1 -1
data/lib/neuroncheck/error.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module NeuronCheckSystem
|
2
|
-
|
3
|
-
|
2
|
+
class ExceptionBase < Exception
|
3
|
+
end
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
class DeclarationError < ExceptionBase
|
6
|
+
end
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
class PluginError < ExceptionBase
|
9
|
+
end
|
10
10
|
end
|
11
11
|
|
12
12
|
class NeuronCheckError < NeuronCheckSystem::ExceptionBase
|
data/lib/neuroncheck/kernel.rb
CHANGED
@@ -6,613 +6,613 @@ require 'neuroncheck/plugin'
|
|
6
6
|
require 'neuroncheck/syntax'
|
7
7
|
|
8
8
|
module NeuronCheckSystem
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
9
|
+
# メソッド追加時のフック処理や、属性宣言メソッドのオーバーライドなどを定義したメインモジュール
|
10
|
+
# NeuronCheckを行いたい対象のモジュールやクラスにextendすることで使用する
|
11
|
+
module Kernel
|
12
|
+
# 属性定義
|
13
|
+
def attr(name, assignable = false)
|
14
|
+
@__neuron_check_method_added_hook_enabled = false
|
15
|
+
begin
|
16
|
+
# 元処理を呼ぶ
|
17
|
+
super
|
18
|
+
|
19
|
+
# NeuronCheck用の属性定義時処理を呼ぶ
|
20
|
+
__neuron_check_attr_defined(__method__, [name], use_reader: true, use_writer: assignable)
|
21
|
+
ensure
|
22
|
+
@__neuron_check_method_added_hook_enabled = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def attr_reader(*names)
|
27
|
+
@__neuron_check_method_added_hook_enabled = false
|
28
|
+
begin
|
29
|
+
# 元処理を呼ぶ
|
30
|
+
super
|
31
|
+
|
32
|
+
# NeuronCheck用の属性定義時処理を呼ぶ
|
33
|
+
__neuron_check_attr_defined(__method__, names, use_reader: true, use_writer: false)
|
34
|
+
ensure
|
35
|
+
@__neuron_check_method_added_hook_enabled = true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def attr_writer(*names)
|
40
|
+
@__neuron_check_method_added_hook_enabled = false
|
41
|
+
begin
|
42
|
+
# 元処理を呼ぶ
|
43
|
+
super
|
44
|
+
|
45
|
+
# NeuronCheck用の属性定義時処理を呼ぶ
|
46
|
+
__neuron_check_attr_defined(__method__, names, use_reader: false, use_writer: true)
|
47
|
+
ensure
|
48
|
+
@__neuron_check_method_added_hook_enabled = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def attr_accessor(*names)
|
53
|
+
@__neuron_check_method_added_hook_enabled = false
|
54
|
+
begin
|
55
|
+
# 元処理を呼ぶ
|
56
|
+
super
|
57
|
+
|
58
|
+
# NeuronCheck用の属性定義時処理を呼ぶ
|
59
|
+
__neuron_check_attr_defined(__method__, names, use_reader: true, use_writer: true)
|
60
|
+
ensure
|
61
|
+
@__neuron_check_method_added_hook_enabled = true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def __neuron_check_attr_defined(used_method_name, names, use_reader: false, use_writer: false)
|
66
|
+
# 直前にNeuronCheck宣言部があれば、その宣言内容を各メソッドへ登録する
|
67
|
+
if (declaration = @__neuron_check_last_declaration) then
|
68
|
+
# 短縮記法による宣言かどうかで分岐
|
69
|
+
if declaration.shorthand then
|
70
|
+
# 短縮記法の場合は、引数が2つ以上宣言されている、もしくは戻り値が宣言されている場合にエラーとする
|
71
|
+
if declaration.arg_matchers.size >= 2 or declaration.return_matcher then
|
72
|
+
raise NeuronCheckSystem::DeclarationError, "expected value must be one for `#{used_method_name}'", declaration.declared_caller_locations
|
73
|
+
end
|
74
|
+
|
75
|
+
# 引数1用のマッチャを属性用のマッチャとみなす
|
76
|
+
target_attr_matcher = declaration.arg_matchers[0]
|
77
|
+
else
|
78
|
+
# 通常の宣言の場合、引数、戻り値の宣言がされている場合はエラーとする
|
79
|
+
if declaration.arg_matchers.size >= 1 or declaration.return_matcher then
|
80
|
+
raise NeuronCheckSystem::DeclarationError, "`args' or `returns' declaration can be used only for method definition, but used for `#{used_method_name}'", declaration.declared_caller_locations
|
81
|
+
end
|
82
|
+
|
83
|
+
target_attr_matcher = declaration.attr_matcher
|
84
|
+
end
|
85
|
+
|
86
|
+
# 属性チェック用モジュールに対する処理
|
87
|
+
@__neuron_check_attr_check_module.module_eval do
|
88
|
+
|
89
|
+
# 属性1つごとに処理
|
90
|
+
names.each do |attr_name|
|
91
|
+
# 属性チェック用モジュールに、readerチェック用のラッパーメソッドを追加する
|
92
|
+
if use_reader then
|
93
|
+
define_method(attr_name) do
|
94
|
+
# 通常の処理を呼び出す
|
95
|
+
val = super()
|
96
|
+
|
97
|
+
# 属性宣言があればチェック処理
|
98
|
+
if target_attr_matcher then
|
99
|
+
unless target_attr_matcher.match?(val, self) then
|
100
|
+
context_caption = "value of attribute `#{self.class.name}##{attr_name}'"
|
101
|
+
raise NeuronCheckError, target_attr_matcher.get_error_message(declaration, context_caption, val), (NeuronCheck.debug? ? caller : caller(1))
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# 属性チェック用モジュールに、writerチェック用のラッパーメソッドを追加する
|
108
|
+
if use_writer then
|
109
|
+
define_method("#{attr_name}=") do |val|
|
110
|
+
# 属性宣言があればチェック処理
|
111
|
+
if target_attr_matcher then
|
112
|
+
unless target_attr_matcher.match?(val, self) then
|
113
|
+
context_caption = "value of attribute `#{self.class.name}##{attr_name}'"
|
114
|
+
raise NeuronCheckError, target_attr_matcher.get_error_message(declaration, context_caption, val, phrase_after_but: 'set'), (NeuronCheck.debug? ? caller : caller(1))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# 通常の処理を呼び出す
|
119
|
+
super(val)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# 属性1つごとに処理
|
127
|
+
names.each do |attr_name|
|
128
|
+
# 登録実行
|
129
|
+
NeuronCheckSystem::ATTR_DECLARATIONS[self][attr_name] = declaration
|
130
|
+
declaration.assigned_class_or_module = self
|
131
|
+
declaration.assigned_attribute_name = attr_name
|
132
|
+
end
|
133
|
+
|
134
|
+
# チェック処理の追加が完了したら、「最後に宣言した内容」を表すクラスインスタンス変数をnilに戻す
|
135
|
+
@__neuron_check_last_declaration = nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# インスタンスメソッド定義を追加したときの処理
|
141
|
+
def method_added(name)
|
142
|
+
# まずは親処理を呼ぶ
|
143
|
+
super
|
144
|
+
|
145
|
+
# メイン処理をコール
|
146
|
+
__neuron_check_method_added_hook(self, name, self.instance_method(name))
|
147
|
+
end
|
148
|
+
|
149
|
+
# 特異メソッド定義を追加したときの処理
|
150
|
+
def singleton_method_added(name)
|
151
|
+
# まずは親処理を呼ぶ
|
152
|
+
super
|
153
|
+
|
154
|
+
# メイン処理をコール
|
155
|
+
__neuron_check_method_added_hook(self.singleton_class, name, self.singleton_class.instance_method(name), self)
|
156
|
+
end
|
157
|
+
|
158
|
+
# メソッド/特異メソッドを定義したときの共通処理
|
159
|
+
def __neuron_check_method_added_hook(target_cls_or_mod, method_name, met, singleton_original_class = nil)
|
160
|
+
singleton = !(singleton_original_class.nil?)
|
161
|
+
|
162
|
+
# メソッド定義時のフックが無効化されている場合は何もしない
|
163
|
+
return unless @__neuron_check_method_added_hook_enabled
|
164
|
+
|
165
|
+
# 直前にNeuronCheck宣言部があれば、その宣言内容を登録する
|
166
|
+
if (declaration = @__neuron_check_last_declaration) then
|
167
|
+
|
168
|
+
# あらかじめ登録と、メソッドやクラスとの紐付けを行っておく
|
169
|
+
# この処理を先に行わないと正しく名前を取得できない
|
170
|
+
NeuronCheckSystem::METHOD_DECLARATIONS[target_cls_or_mod][method_name] = declaration
|
171
|
+
declaration.assigned_class_or_module = target_cls_or_mod
|
172
|
+
declaration.assigned_method = met
|
173
|
+
declaration.assigned_singleton_original_class = singleton_original_class
|
174
|
+
|
175
|
+
|
176
|
+
# 宣言に引数チェックが含まれている場合、宣言部の引数の数が、実際のメソッドの引数の数を超えていないかをチェック
|
177
|
+
# 超えていれば宣言エラーとする
|
178
|
+
if declaration.arg_matchers.size > met.parameters.size then
|
179
|
+
raise NeuronCheckSystem::DeclarationError, "given arguments number of ##{method_name} greater than method definition - expected #{met.parameters.size} args, but #{declaration.arg_matchers.size} args were declared"
|
180
|
+
end
|
181
|
+
|
182
|
+
# パラメータの中にブロック型の引数が含まれているが
|
183
|
+
# そのパラメータが、anyでもblockでもない型である場合はエラー
|
184
|
+
met.parameters.each_with_index do |param_info, def_param_index|
|
185
|
+
param_type, param_name = param_info
|
186
|
+
if param_type == :block and (matcher = declaration.arg_matchers[def_param_index]) then
|
187
|
+
next if matcher.respond_to?(:keyword_name) and matcher.keyword_name == 'any'
|
188
|
+
next if matcher.respond_to?(:keyword_name) and matcher.keyword_name == 'block'
|
189
|
+
|
190
|
+
context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
|
191
|
+
raise NeuronCheckSystem::DeclarationError, "#{context_caption} is block argument - it can be specified only keyword `any' or `block'"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# 特異メソッドでなく、メソッド名が「initialize」であるにもかかわらず、returns宣言が含まれている場合はエラー
|
196
|
+
if not singleton and method_name == :initialize and declaration.return_matcher then
|
197
|
+
raise NeuronCheckSystem::DeclarationError, "returns declaration cannot be used with `#initialize' method"
|
198
|
+
end
|
199
|
+
|
200
|
+
# チェック処理の追加が完了したら、「最後に宣言した内容」を表すクラスインスタンス変数をnilに戻す
|
201
|
+
@__neuron_check_last_declaration = nil
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
# チェック処理を差し込むためのTracePointを作成
|
208
|
+
TRACE_POINT = TracePoint.new(:call, :return) do |tp|
|
209
|
+
cls = tp.defined_class
|
210
|
+
|
211
|
+
# メソッドに紐付けられた宣言を取得。取得できなければ終了
|
212
|
+
decls = METHOD_DECLARATIONS[cls]
|
213
|
+
next unless decls
|
214
|
+
|
215
|
+
decl = decls[tp.method_id]
|
216
|
+
next unless decl
|
217
|
+
|
218
|
+
# メソッドの宣言情報を取得 (宣言があれば、メソッドの定義情報も必ず格納されているはずなので、取得成否チェックはなし)
|
219
|
+
met = decl.assigned_method
|
220
|
+
|
221
|
+
# ここからの処理はイベント種別で分岐
|
222
|
+
case tp.event
|
223
|
+
when :call # タイプが「メソッドの呼び出し」の場合の処理
|
224
|
+
self.trace_method_call(tp.self, decl, met, tp.binding)
|
225
|
+
when :return # タイプが「メソッドの終了」の場合の処理
|
226
|
+
|
227
|
+
# 「次の1回はスキップする」フラグがONであればスキップ
|
228
|
+
if @skip_next_return then
|
229
|
+
@skip_next_return = false
|
230
|
+
next
|
231
|
+
else
|
232
|
+
|
233
|
+
ex = self.trace_method_return(tp.self, decl, met, tp.binding, tp.return_value)
|
234
|
+
if ex then
|
235
|
+
# raiseで例外を脱出した場合、なぜかもう1回returnイベントが呼ばれるため、次の1回はスキップするように設定
|
236
|
+
@skip_next_return = true
|
237
|
+
|
238
|
+
# 例外を発生させる
|
239
|
+
raise ex
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
TRACE_POINT.enable
|
245
|
+
|
246
|
+
# 全メソッド宣言のリスト
|
247
|
+
METHOD_DECLARATIONS = {}
|
248
|
+
|
249
|
+
# 全属性制限と対応するメソッド情報のリスト
|
250
|
+
ATTR_DECLARATIONS = {}
|
251
|
+
|
252
|
+
# bindingクラスに local_variable_get が定義されているかどうかを判定し、その判定結果を定数に記録 (Ruby 2.1以降では定義されているはず)
|
253
|
+
LOCAL_VARIABLE_GET_USABLE = (binding.respond_to?(:local_variable_get))
|
254
|
+
|
255
|
+
# 指定したbindingから、指定した名前のローカル変数を取得する (Binding#local_variable_getが定義されているかどうかで実装を変える)
|
256
|
+
def self.get_local_variable_from_binding(target_binding, var_name)
|
257
|
+
if LOCAL_VARIABLE_GET_USABLE then
|
258
|
+
target_binding.local_variable_get(var_name)
|
259
|
+
else
|
260
|
+
# local_variable_getが使えない時は、代わりにevalを使用して取得
|
261
|
+
target_binding.eval(var_name.to_s)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# メソッド呼び出し時のチェック処理 (現段階ではattrメソッドの呼び出しでは発生しないことに注意。Ruby 2.1で確認)
|
266
|
+
def self.trace_method_call(method_self, declaration, met, method_binding)
|
267
|
+
param_values = {}
|
268
|
+
|
269
|
+
# 大域脱出できるようにcatchを使用 (バックトレースを正しく取得するための処置)
|
270
|
+
error_msg = catch(:neuron_check_error_tag) do
|
271
|
+
|
272
|
+
# 対象メソッドの引数1つごとに処理
|
273
|
+
met.parameters.each_with_index do |param_info, def_param_index|
|
274
|
+
param_type, param_name = param_info
|
275
|
+
|
276
|
+
# 実際に渡された引数の値を取得 (デフォルト値の処理も考慮する)
|
277
|
+
param_value = get_local_variable_from_binding(method_binding, param_name)
|
278
|
+
param_values[param_name] = param_value
|
279
|
+
|
280
|
+
# 指定位置に対応するマッチャが登録されている場合のみ処理
|
281
|
+
if (matcher = declaration.arg_matchers[def_param_index]) then
|
282
|
+
|
283
|
+
# 引数の種類で処理を分岐
|
284
|
+
case param_type
|
285
|
+
when :key # キーワード引数
|
286
|
+
unless matcher.match?(param_value, self) then
|
287
|
+
context_caption = "argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
|
288
|
+
@last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
|
289
|
+
|
290
|
+
throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, param_value)
|
291
|
+
end
|
292
|
+
|
293
|
+
when :keyrest # キーワード引数の可変長部分
|
294
|
+
# 可変長部分1つごとにチェック
|
295
|
+
param_value.each_pair do |key, value|
|
296
|
+
unless matcher.match?(value, self) then
|
297
|
+
context_caption = "argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
|
298
|
+
@last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
|
299
|
+
|
300
|
+
throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, value)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
when :rest # 可変長部分
|
305
|
+
# 可変長引数であれば、受け取った引数1つごとにチェック
|
306
|
+
param_value.each_with_index do |val, i|
|
307
|
+
unless matcher.match?(val, self) then
|
308
|
+
context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1 + i)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
|
309
|
+
@last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
|
310
|
+
|
311
|
+
throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, val)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
else # 通常の引数/ブロック引数
|
316
|
+
unless matcher.match?(param_value, self) then
|
317
|
+
context_caption = "#{NeuronCheckSystem::Utils.ordinalize(def_param_index + 1)} argument `#{param_name}' of `#{declaration.signature_caption_name_only}'"
|
318
|
+
@last_argument_error_info = [method_self, met.name] # 引数チェックエラーの発生情報を記録
|
319
|
+
|
320
|
+
throw :neuron_check_error_tag, matcher.get_error_message(declaration, context_caption, param_value)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
# 事前条件があれば実行
|
328
|
+
if declaration.precond then
|
329
|
+
# コンテキストを生成
|
330
|
+
context = make_cond_block_context(method_self, 'precond', param_values, declaration.precond_allow_instance_method)
|
331
|
+
|
332
|
+
# 条件文実行
|
333
|
+
context.instance_exec(&(declaration.precond))
|
334
|
+
end
|
335
|
+
|
336
|
+
# 最後まで正常実行した場合はnilを返す
|
337
|
+
nil
|
338
|
+
end # catch
|
339
|
+
|
340
|
+
# エラーメッセージがあればNeuronCheckError発生
|
341
|
+
if error_msg then
|
342
|
+
raise NeuronCheckError, error_msg, (NeuronCheck.debug? ? caller : caller(3))
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# メソッド終了時のチェック処理
|
347
|
+
def self.trace_method_return(method_self, declaration, met, method_binding, return_value)
|
348
|
+
# 処理中に例外が発生した場合、TracePointがreturnとして検知してしまうため
|
349
|
+
# それを防ぐために例外を自前でキャッチ
|
350
|
+
begin
|
351
|
+
# 大域脱出できるようにcatchを使用 (バックトレースを正しく取得するための処置)
|
352
|
+
error_msg = catch(:neuron_check_error_tag) do
|
353
|
+
|
354
|
+
# 最後に引数チェックエラーが発生しており、selfとメソッド名が同じものであれば、return値のチェックをスキップ
|
355
|
+
# (NeuronCheckError例外を発生させたときにもreturnイベントは発生してしまうため、その対策として)
|
356
|
+
if @last_argument_error_info and @last_argument_error_info[0].equal?(method_self) and @last_argument_error_info[1] == met.name then
|
357
|
+
@last_argument_error_info = nil
|
358
|
+
return
|
359
|
+
end
|
360
|
+
|
361
|
+
param_values = {}
|
362
|
+
|
363
|
+
# 対象メソッドの引数1つごとに処理
|
364
|
+
met.parameters.each_with_index do |param_info, def_param_index|
|
365
|
+
_, param_name = param_info
|
366
|
+
|
367
|
+
# 実際に渡された引数の値を取得 (デフォルト値の処理も考慮する)
|
368
|
+
param_value = get_local_variable_from_binding(method_binding, param_name)
|
369
|
+
param_values[param_name] = param_value
|
370
|
+
end
|
371
|
+
|
372
|
+
# 戻り値チェック
|
373
|
+
if (matcher = declaration.return_matcher) then
|
374
|
+
# チェック処理
|
375
|
+
unless matcher.match?(return_value, method_self) then
|
376
|
+
# エラー
|
377
|
+
throw :neuron_check_error_tag, matcher.get_error_message(declaration, "return value of `#{declaration.signature_caption_name_only}'", return_value)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# 事後条件があれば実行
|
382
|
+
if declaration.postcond then
|
383
|
+
# コンテキストを生成
|
384
|
+
context = make_cond_block_context(method_self, 'postcond', param_values, declaration.postcond_allow_instance_method)
|
385
|
+
|
386
|
+
# 条件文実行
|
387
|
+
context.instance_exec(return_value, &(declaration.postcond))
|
388
|
+
end
|
389
|
+
|
390
|
+
# 最後まで正常実行した場合はnilを返す
|
391
|
+
nil
|
392
|
+
end # catch
|
393
|
+
|
394
|
+
# エラーメッセージがあればNeuronCheckError発生
|
395
|
+
if error_msg then
|
396
|
+
raise NeuronCheckError, error_msg, (NeuronCheck.debug? ? caller : caller(3))
|
397
|
+
end
|
398
|
+
rescue Exception
|
399
|
+
# 例外が発生した場合はその例外オブジェクトを返す
|
400
|
+
return $!
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
# 事前条件/事後条件の実行用コンテキストを構築する
|
405
|
+
def self.make_cond_block_context(method_self, context_caption, param_values, allow_instance_method)
|
406
|
+
# ローカル変数指定用の無名モジュールを作成
|
407
|
+
local_mod = Module.new
|
408
|
+
local_mod.module_eval do
|
409
|
+
# ローカル変数取得用のメソッドを定義する
|
410
|
+
param_values.each_pair do |var_name, value|
|
411
|
+
define_method(var_name) do
|
412
|
+
return value
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
# ブロック実行用のコンテキストを生成し、ローカル変数保持モジュールを差し込む
|
418
|
+
context = CondBlockContext.new(context_caption, method_self, allow_instance_method)
|
419
|
+
context.extend local_mod
|
420
|
+
|
421
|
+
# 対象のオブジェクトが持つ全てのインスタンス変数を、コンテキストへ引き渡す
|
422
|
+
method_self.instance_variables.each do |var_name|
|
423
|
+
val = method_self.instance_variable_get(var_name)
|
424
|
+
context.instance_variable_set(var_name, val)
|
425
|
+
end
|
426
|
+
|
427
|
+
return context
|
428
|
+
end
|
429
|
+
|
430
|
+
# モジュール/クラス1つに対して、NeuronCheck用の初期化を行う
|
431
|
+
def self.initialize_module_for_neuron_check(mod_or_class)
|
432
|
+
# 2回目以降の初期化であれば何もせずにスルー
|
433
|
+
if mod_or_class.instance_variable_get(:@__neuron_check_initialized) then
|
434
|
+
return
|
435
|
+
end
|
436
|
+
|
437
|
+
# 宣言とメソッド情報を格納するためのHashを更新
|
438
|
+
NeuronCheckSystem::METHOD_DECLARATIONS[mod_or_class] = {}
|
439
|
+
NeuronCheckSystem::METHOD_DECLARATIONS[mod_or_class.singleton_class] = {} # 特異メソッド用のクラスも追加
|
440
|
+
NeuronCheckSystem::ATTR_DECLARATIONS[mod_or_class] = {}
|
441
|
+
|
442
|
+
# 対象のModule/Classに対する処理
|
443
|
+
mod_or_class.instance_eval do
|
444
|
+
# 最後に宣言された内容を保持するクラスインスタンス変数(モジュールインスタンス変数)を定義
|
445
|
+
@__neuron_check_last_declaration = nil
|
446
|
+
# メソッド定義時のフックを一時的に無効化するフラグ
|
447
|
+
@__neuron_check_method_added_hook_enabled = true
|
448
|
+
# 属性の処理を上書きするために差し込む無名モジュールを定義 (prependする)
|
449
|
+
@__neuron_check_attr_check_module = Module.new
|
450
|
+
prepend @__neuron_check_attr_check_module
|
451
|
+
|
452
|
+
# メソッド定義時のフックなどを定義したモジュールをextend
|
453
|
+
extend NeuronCheckSystem::Kernel
|
454
|
+
|
455
|
+
# initialize済みフラグON
|
456
|
+
@__neuron_check_initialized = true # 初期化
|
457
|
+
end
|
458
|
+
end
|
459
459
|
end
|
460
460
|
|
461
461
|
# ユーザーが利用するメインモジュール。extend処理や、有効化/無効化などに使用する
|
462
462
|
module NeuronCheck
|
463
463
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
464
|
+
# チェック実行フラグ
|
465
|
+
@enabled = true
|
466
|
+
def self.enabled?; @enabled; end
|
467
|
+
|
468
|
+
# デバッグフラグ
|
469
|
+
@debug = false
|
470
|
+
def self.debug; @debug; end
|
471
|
+
def self.debug?; @debug; end
|
472
|
+
def self.debug=(v); @debug = v; end
|
473
|
+
|
474
|
+
# 無効化
|
475
|
+
def self.disable
|
476
|
+
if block_given? then
|
477
|
+
# ブロックが渡された場合は、ブロック実行中のみチェックを無効化
|
478
|
+
begin
|
479
|
+
disable
|
480
|
+
yield
|
481
|
+
ensure
|
482
|
+
enable
|
483
|
+
end
|
484
|
+
else
|
485
|
+
@enabled = false
|
486
|
+
NeuronCheckSystem::TRACE_POINT.disable # メソッド呼び出しフックを無効化
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
# 無効化されたチェックを、再度有効化
|
491
|
+
def self.enable
|
492
|
+
if block_given? then
|
493
|
+
# ブロックが渡された場合は、ブロック実行中のみチェックを有効か
|
494
|
+
begin
|
495
|
+
enable
|
496
|
+
yield
|
497
|
+
ensure
|
498
|
+
disable
|
499
|
+
end
|
500
|
+
else
|
501
|
+
@enabled = true
|
502
|
+
NeuronCheckSystem::TRACE_POINT.enable # メソッド呼び出しフックを有効化
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
# 単体チェック
|
507
|
+
def self.match?(value, &expected_block)
|
508
|
+
# 宣言ブロック実行用のコンテキストを作成
|
509
|
+
context = NeuronCheckSystem::DeclarationContext.new
|
510
|
+
# 宣言ブロックの内容を実行
|
511
|
+
expected = context.instance_eval(&expected_block)
|
512
|
+
|
513
|
+
matcher = NeuronCheckSystem.get_appropriate_matcher(expected, [])
|
514
|
+
return matcher.match?(value, nil)
|
515
|
+
end
|
516
|
+
|
517
|
+
# 単体チェック
|
518
|
+
def self.check(value, &expected_block)
|
519
|
+
# 宣言ブロック実行用のコンテキストを作成
|
520
|
+
context = NeuronCheckSystem::DeclarationContext.new
|
521
|
+
# 宣言ブロックの内容を実行
|
522
|
+
expected = context.instance_eval(&expected_block)
|
523
|
+
|
524
|
+
matcher = NeuronCheckSystem.get_appropriate_matcher(expected, [])
|
525
|
+
unless matcher.match?(value, nil) then
|
526
|
+
raise NeuronCheckError, matcher.get_error_message(nil, 'value', value), (NeuronCheck.debug? ? caller : caller(1))
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
# 宣言情報の出力
|
531
|
+
def self.get_declarations_as_json(ignore_unnamed_modules: false)
|
532
|
+
re = {'instance_methods' => {}, 'singleton_methods' => {}, 'attributes' => {}}
|
533
|
+
|
534
|
+
NeuronCheckSystem::METHOD_DECLARATIONS.each_pair do |cls_or_mod, data|
|
535
|
+
data.each_pair do |method_name, decl|
|
536
|
+
method_type = (decl.assinged_to_singleton_method? ? 'singleton_methods' : 'instance_methods')
|
537
|
+
target_mod_or_class = (decl.assinged_to_singleton_method? ? decl.assigned_singleton_original_class : decl.assigned_class_or_module)
|
538
|
+
key = target_mod_or_class.name
|
539
|
+
|
540
|
+
# 無名モジュール/クラスの場合の対応
|
541
|
+
if key.nil? and not ignore_unnamed_modules then
|
542
|
+
key = target_mod_or_class.inspect
|
543
|
+
end
|
544
|
+
|
545
|
+
if key then
|
546
|
+
re[method_type][key] ||= {}
|
547
|
+
re[method_type][key][method_name.to_s] = decl.meta_info_as_json
|
548
|
+
end
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
NeuronCheckSystem::ATTR_DECLARATIONS.each_pair do |cls_or_mod, data|
|
553
|
+
key = cls_or_mod.name
|
554
|
+
|
555
|
+
# 無名モジュール/クラスの場合の対応
|
556
|
+
if key.nil? and not ignore_unnamed_modules then
|
557
|
+
key = cls_or_mod.inspect
|
558
|
+
end
|
559
|
+
|
560
|
+
data.each_pair do |method_name, decl|
|
561
|
+
re['attributes'][key] ||= {}
|
562
|
+
re['attributes'][key][method_name.to_s] = decl.meta_info_as_json
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
re
|
567
|
+
end
|
568
|
+
|
569
|
+
# extend時処理
|
570
|
+
def self.extended(mod_or_class)
|
571
|
+
# extend対象がモジュールでもクラスでもなければエラー
|
572
|
+
unless mod_or_class.kind_of?(Module) then
|
573
|
+
raise ScriptError, "NeuronCheck can be extended only to Class or Module"
|
574
|
+
end
|
575
|
+
|
576
|
+
# 2回目以降のextendであれば何もせずにスルー
|
577
|
+
if mod_or_class.instance_variable_get(:@__neuron_check_extended) then
|
578
|
+
return
|
579
|
+
end
|
580
|
+
|
581
|
+
# まずはNeuronCheck用の初期化
|
582
|
+
NeuronCheckSystem.initialize_module_for_neuron_check(mod_or_class)
|
583
|
+
|
584
|
+
# 対象のModule/Classに対する処理
|
585
|
+
mod_or_class.instance_eval do
|
586
|
+
# Module/Classに対して宣言用のメソッドを追加する
|
587
|
+
extend NeuronCheckSystem::DeclarationMethods
|
588
|
+
|
589
|
+
# extend済みフラグON
|
590
|
+
@__neuron_check_extended = true
|
591
|
+
end
|
592
|
+
end
|
593
593
|
end
|
594
594
|
|
595
595
|
|
596
596
|
|
597
597
|
# トップレベル関数用の特殊なndecl
|
598
598
|
self.instance_eval do
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
599
|
+
def ndecl(*expecteds, &block)
|
600
|
+
decl_caller = caller(2, 1)
|
601
|
+
|
602
|
+
# Objectクラスへの宣言とみなす
|
603
|
+
Object.class_eval do
|
604
|
+
# extend NeuronCheckが実行されていない場合、NeuronCheck用の初期化を自動実行
|
605
|
+
unless @__neuron_check_extended then
|
606
|
+
extend NeuronCheck
|
607
|
+
end
|
608
|
+
|
609
|
+
# メイン処理実行
|
610
|
+
__neuroncheck_ndecl_main(expecteds, block, decl_caller)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
alias ndeclare ndecl
|
615
|
+
alias ncheck ndecl
|
616
|
+
alias ntypesig ndecl
|
617
|
+
alias nsig ndecl
|
618
618
|
end
|