rtype-legacy 0.0.4-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,436 @@
1
+ if Object.const_defined?(:RUBY_ENGINE)
2
+ case RUBY_ENGINE
3
+ when "jruby"
4
+ begin
5
+ require 'java'
6
+ require 'rtype/legacy/rtype_legacy_java'
7
+ # puts "Rtype Legacy with Java extension"
8
+ rescue LoadError
9
+ # puts "Rtype Legacy without native extension"
10
+ end
11
+ when "ruby"
12
+ begin
13
+ require 'rtype/legacy/rtype_legacy_native'
14
+ # puts "Rtype Legacy with C native extension"
15
+ rescue LoadError
16
+ # puts "Rtype Legacy without native extension"
17
+ end
18
+ end
19
+ end
20
+
21
+ require_relative 'rtype_component'
22
+ require_relative 'method_annotator'
23
+ require_relative 'core_ext'
24
+ require_relative 'legacy/version'
25
+ require_relative 'type_signature_error'
26
+ require_relative 'argument_type_error'
27
+ require_relative 'return_type_error'
28
+ require_relative 'type_signature'
29
+ require_relative 'behavior'
30
+
31
+ module Rtype
32
+ extend self
33
+
34
+ # This is just 'information'
35
+ # Any change of this doesn't affect type checking
36
+ @@type_signatures = Hash.new
37
+
38
+ # Makes the method typed
39
+ # @param owner Owner of the method
40
+ # @param [#to_sym] method_name
41
+ # @param [Hash] type_sig_info A type signature. e.g. `[Integer, Float] => Float`
42
+ # @param [Boolean] singleton Whether the method is singleton method
43
+ # @return [void]
44
+ #
45
+ # @raise [ArgumentError] If method_name is nil, keyword argument signature is not empty, or singleton is not a boolean
46
+ # @raise [TypeSignatureError] If type_sig_info is invalid
47
+ def define_typed_method(owner, method_name, type_sig_info, singleton)
48
+ method_name = method_name.to_sym
49
+ raise ArgumentError, "method_name is nil" if method_name.nil?
50
+ raise ArgumentError, "singleton must be a boolean" unless singleton.is_a?(Boolean)
51
+ assert_valid_type_sig(type_sig_info)
52
+
53
+ el = type_sig_info.first
54
+ arg_sig = el[0]
55
+ return_sig = el[1]
56
+
57
+ if arg_sig.is_a?(Array)
58
+ expected_args = arg_sig.dup
59
+ if expected_args.last.is_a?(Hash)
60
+ kwargs = expected_args.pop
61
+ # empty kwargs signature
62
+ else
63
+ # empty kwargs signature
64
+ end
65
+ elsif arg_sig.is_a?(Hash)
66
+ # empty kwargs signature
67
+ expected_args = []
68
+ end
69
+
70
+ sig = TypeSignature.new
71
+ sig.argument_type = arg_sig
72
+ sig.return_type = return_sig
73
+ unless @@type_signatures.key?(owner)
74
+ @@type_signatures[owner] = {}
75
+ end
76
+ @@type_signatures[owner][method_name] = sig
77
+
78
+ redefine_method_to_typed(owner, method_name, expected_args, return_sig, singleton)
79
+ end
80
+
81
+ # @param owner Owner of the accessor
82
+ # @param [#to_sym] name
83
+ # @param type_behavior A type behavior. e.g. Integer
84
+ # @param [Boolean] singleton Whether the method is singleton method
85
+ # @return [void]
86
+ #
87
+ # @raise [ArgumentError] If name is nil
88
+ # @raise [TypeSignatureError]
89
+ def define_typed_accessor(owner, name, type_behavior, singleton)
90
+ define_typed_reader(owner, name, type_behavior, singleton)
91
+ define_typed_writer(owner, name, type_behavior, singleton)
92
+ end
93
+
94
+ # @param owner Owner of the getter
95
+ # @param [#to_sym] name
96
+ # @param type_behavior A type behavior. e.g. Integer
97
+ # @param [Boolean] singleton Whether the method is singleton method
98
+ # @return [void]
99
+ #
100
+ # @raise [ArgumentError] If name is nil
101
+ # @raise [TypeSignatureError]
102
+ def define_typed_reader(owner, name, type_behavior, singleton)
103
+ raise ArgumentError, "name is nil" if name.nil?
104
+ valid?(type_behavior, nil)
105
+ define_typed_method owner, name.to_sym, {[] => type_behavior}, singleton
106
+ end
107
+
108
+ # @param owner Owner of the setter
109
+ # @param [#to_sym] name
110
+ # @param type_behavior A type behavior. e.g. Integer
111
+ # @param [Boolean] singleton Whether the method is singleton method
112
+ # @return [void]
113
+ #
114
+ # @raise [ArgumentError] If name is nil
115
+ # @raise [TypeSignatureError]
116
+ def define_typed_writer(owner, name, type_behavior, singleton)
117
+ raise ArgumentError, "name is nil" if name.nil?
118
+ valid?(type_behavior, nil)
119
+ define_typed_method owner, :"#{name.to_sym}=", {[type_behavior] => Any}, singleton
120
+ end
121
+
122
+ # This is just 'information'
123
+ # Any change of this doesn't affect type checking
124
+ #
125
+ # @return [Hash]
126
+ # @note type_signatures[owner][method_name]
127
+ def type_signatures
128
+ @@type_signatures
129
+ end
130
+
131
+ # @param [Integer] idx
132
+ # @param expected A type behavior
133
+ # @param value
134
+ # @return [String] A error message
135
+ #
136
+ # @raise [ArgumentError] If expected is invalid
137
+ def arg_type_error_message(idx, expected, value)
138
+ "#{arg_message(idx)}\n" + type_error_message(expected, value)
139
+ end
140
+
141
+ # @return [String]
142
+ def arg_message(idx)
143
+ "for #{ordinalize_number(idx+1)} argument:"
144
+ end
145
+
146
+ # Returns a error message for the pair of type behavior and value
147
+ #
148
+ # @param expected A type behavior
149
+ # @param value
150
+ # @return [String] error message
151
+ #
152
+ # @note This method doesn't check the value is valid
153
+ # @raise [TypeSignatureError] If expected is invalid
154
+ def type_error_message(expected, value)
155
+ case expected
156
+ when Rtype::Behavior::Base
157
+ expected.error_message(value)
158
+ when Module
159
+ "Expected #{value.inspect} to be a #{expected}"
160
+ when Symbol
161
+ "Expected #{value.inspect} to respond to :#{expected}"
162
+ when Regexp
163
+ "Expected stringified #{value.inspect} to match regexp #{expected.inspect}"
164
+ when Range
165
+ "Expected #{value.inspect} to be included in range #{expected.inspect}"
166
+ when Array
167
+ arr = expected.map { |e| type_error_message(e, value) }
168
+ arr.join("\nOR ")
169
+ when Hash
170
+ if value.is_a?(Hash)
171
+ arr = []
172
+ expected.each do |k, v|
173
+ if v.is_a?(Array) || v.is_a?(Hash)
174
+ arr << "- #{k} : {\n" + type_error_message(v, value[k]) + "\n}"
175
+ else
176
+ arr << "- #{k} : " + type_error_message(v, value[k])
177
+ end
178
+ end
179
+ "Expected #{value.inspect} to be a hash with #{expected.length} elements:\n" + arr.join("\n")
180
+ else
181
+ "Expected #{value.inspect} to be a hash"
182
+ end
183
+ when Proc
184
+ "Expected #{value.inspect} to return a truthy value for proc #{expected}"
185
+ when true
186
+ "Expected #{value.inspect} to be a truthy value"
187
+ when false
188
+ "Expected #{value.inspect} to be a falsy value"
189
+ when nil # for return
190
+ "Expected #{value.inspect} to be nil"
191
+ else
192
+ raise TypeSignatureError, "Invalid type behavior #{expected}"
193
+ end
194
+ end
195
+
196
+ # Checks the type signature is valid
197
+ #
198
+ # e.g.
199
+ # `[Integer] => Any` is valid.
200
+ # `[Integer]` or `Any` are invalid
201
+ #
202
+ # @param sig A type signature
203
+ # @raise [TypeSignatureError] If sig is invalid
204
+ def assert_valid_type_sig(sig)
205
+ unless sig.is_a?(Hash)
206
+ raise TypeSignatureError, "Invalid type signature: type signature is not hash"
207
+ end
208
+ if sig.empty?
209
+ raise TypeSignatureError, "Invalid type signature: type signature is empty hash"
210
+ end
211
+ assert_valid_arguments_type_sig(sig.first[0])
212
+ assert_valid_return_type_sig(sig.first[1])
213
+ end
214
+
215
+ # Checks the arguments type signature is valid
216
+ #
217
+ # e.g.
218
+ # `[Integer]`, `{key: "value"}` are valid (the second is keyword argument signature and ignored in rtype-legacy).
219
+ # `Integer` is invalid.
220
+ #
221
+ # @param sig A arguments type signature
222
+ # @raise [TypeSignatureError] If sig is invalid
223
+ def assert_valid_arguments_type_sig(sig)
224
+ if sig.is_a?(Array)
225
+ sig = sig.dup
226
+ if sig.last.is_a?(Hash)
227
+ kwargs = sig.pop
228
+ else
229
+ kwargs = {}
230
+ end
231
+ sig.each { |e| assert_valid_argument_type_sig_element(e) }
232
+ unless kwargs.empty?
233
+ raise TypeSignatureError, "Invalid type signature: keyword arguments must be empty"
234
+ end
235
+ elsif sig.is_a?(Hash)
236
+ unless kwargs.empty?
237
+ raise TypeSignatureError, "Invalid type signature: keyword arguments must be empty"
238
+ end
239
+ else
240
+ raise TypeSignatureError, "Invalid type signature: arguments type signature is neither array nor hash"
241
+ end
242
+ end
243
+
244
+ # Checks the type behavior is valid
245
+ #
246
+ # @param sig A type behavior
247
+ # @raise [TypeSignatureError] If sig is invalid
248
+ def assert_valid_argument_type_sig_element(sig)
249
+ case sig
250
+ when Rtype::Behavior::Base
251
+ when Module
252
+ when Symbol
253
+ when Regexp
254
+ when Range
255
+ when Array
256
+ sig.each do |e|
257
+ assert_valid_argument_type_sig_element(e)
258
+ end
259
+ when Hash
260
+ sig.each_value do |e|
261
+ assert_valid_argument_type_sig_element(e)
262
+ end
263
+ when Proc
264
+ when true
265
+ when false
266
+ when nil
267
+ else
268
+ raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{sig}"
269
+ end
270
+ end
271
+
272
+ # @see #assert_valid_argument_type_sig_element
273
+ def assert_valid_return_type_sig(sig)
274
+ assert_valid_argument_type_sig_element(sig)
275
+ end
276
+
277
+ unless respond_to?(:valid?)
278
+ # Checks the value is valid for the type behavior
279
+ #
280
+ # @param expected A type behavior
281
+ # @param value
282
+ # @return [Boolean]
283
+ #
284
+ # @raise [TypeSignatureError] If expected is invalid
285
+ def valid?(expected, value)
286
+ case expected
287
+ when Module
288
+ value.is_a? expected
289
+ when Symbol
290
+ value.respond_to? expected
291
+ when Regexp
292
+ !!(expected =~ value.to_s)
293
+ when Range
294
+ expected.include?(value)
295
+ when Hash
296
+ return false unless value.is_a?(Hash)
297
+ return false unless expected.keys == value.keys
298
+ expected.all? { |k, v| valid?(v, value[k]) }
299
+ when Array
300
+ expected.any? { |e| valid?(e, value) }
301
+ when Proc
302
+ !!expected.call(value)
303
+ when true
304
+ !!value
305
+ when false
306
+ !value
307
+ when Rtype::Behavior::Base
308
+ expected.valid? value
309
+ when nil
310
+ value.nil?
311
+ else
312
+ raise TypeSignatureError, "Invalid type signature: Unknown type behavior #{expected}"
313
+ end
314
+ end
315
+ end
316
+
317
+ unless respond_to?(:assert_arguments_type)
318
+ # Validates arguments
319
+ #
320
+ # @param [Array] expected_args A type signature for non-keyword arguments
321
+ # @param [Array] args
322
+ # @return [void]
323
+ #
324
+ # @raise [TypeSignatureError] If expected_args is invalid
325
+ # @raise [ArgumentTypeError] If args is invalid
326
+ def assert_arguments_type(expected_args, args)
327
+ e_len = expected_args.length
328
+ # `length.times` is faster than `each_with_index`
329
+ args.length.times do |i|
330
+ break if i >= e_len
331
+ expected = expected_args[i]
332
+ value = args[i]
333
+ unless valid?(expected, value)
334
+ raise ArgumentTypeError, "#{arg_message(i)}\n" + type_error_message(expected, value)
335
+ end
336
+ end
337
+ nil
338
+ end
339
+ end
340
+
341
+ # Validates result
342
+ #
343
+ # @param expected A type behavior
344
+ # @param result
345
+ # @return [void]
346
+ #
347
+ # @raise [TypeSignatureError] If expected is invalid
348
+ # @raise [ReturnTypeError] If result is invalid
349
+ unless respond_to?(:assert_return_type)
350
+ def assert_return_type(expected, result)
351
+ unless valid?(expected, result)
352
+ raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
353
+ end
354
+ nil
355
+ end
356
+ end
357
+
358
+ private
359
+ # @param owner
360
+ # @param [Symbol] method_name
361
+ # @param [Array] expected_args
362
+ # @param return_sig
363
+ # @param [Boolean] singleton
364
+ # @return [void]
365
+ def redefine_method_to_typed(owner, method_name, expected_args, return_sig, singleton)
366
+ compo = owner.send(:_rtype_component)
367
+
368
+ methods = owner.instance_methods if !singleton
369
+ methods = owner.methods if singleton
370
+
371
+ if methods.include?(method_name)
372
+ =begin
373
+ unless compo.has_old?(method_name, singleton)
374
+ old_method = owner.instance_method(method_name) if !singleton
375
+ old_method = owner.method(method_name) if singleton
376
+ compo.set_old(method_name, singleton, old_method)
377
+ end
378
+ =end
379
+ if compo.has_old?(method_name, singleton)
380
+ old_method = compo.get_old(method_name, singleton)
381
+ else
382
+ old_method = owner.instance_method(method_name) if !singleton
383
+ old_method = owner.method(method_name) if singleton
384
+ compo.set_old(method_name, singleton, old_method)
385
+ end
386
+ else # Undefined method
387
+ compo.add_undef(method_name, expected_args, return_sig, singleton)
388
+ return
389
+ end
390
+
391
+ owner = owner.singleton_class if singleton
392
+ priv = owner.private_method_defined?(method_name)
393
+ prot = owner.protected_method_defined?(method_name)
394
+
395
+ if !singleton
396
+ # `send` is faster than `method(...).call`
397
+ owner.send :define_method, method_name do |*args, &block|
398
+ ::Rtype::assert_arguments_type(expected_args, args)
399
+ # result = compo.get_old(method_name, singleton).bind(self).call(*args, &block)
400
+ result = old_method.bind(self).call(*args, &block)
401
+ ::Rtype::assert_return_type(return_sig, result)
402
+ result
403
+ end
404
+ else
405
+ owner.send :define_method, method_name do |*args, &block|
406
+ ::Rtype::assert_arguments_type(expected_args, args)
407
+ # result = compo.get_old(method_name, singleton).call(*args, &block)
408
+ result = old_method.call(*args, &block)
409
+ ::Rtype::assert_return_type(return_sig, result)
410
+ result
411
+ end
412
+ end
413
+
414
+ if priv
415
+ owner.send(:private, method_name)
416
+ elsif prot
417
+ owner.send(:protected, method_name)
418
+ end
419
+ nil
420
+ end
421
+
422
+ # @param [Integer] num
423
+ # @return [String]
424
+ def ordinalize_number(num)
425
+ if (11..13).include?(num % 100)
426
+ "#{num}th"
427
+ else
428
+ case num % 10
429
+ when 1; "#{num}st"
430
+ when 2; "#{num}nd"
431
+ when 3; "#{num}rd"
432
+ else "#{num}th"
433
+ end
434
+ end
435
+ end
436
+ end
@@ -0,0 +1,55 @@
1
+ module Rtype
2
+ module MethodAnnotator
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def method_added(name)
9
+ if @_rtype_component
10
+ compo = @_rtype_component
11
+
12
+ if compo.annotation_mode && !compo.ignoring
13
+ if compo.has_undef?(name, false)
14
+ compo.remove_undef(name, false)
15
+ end
16
+
17
+ compo.ignoring = true
18
+ ::Rtype::define_typed_method(self, name, compo.annotation_type_sig, false)
19
+ compo.annotation_mode = false
20
+ compo.annotation_type_sig = nil
21
+ compo.ignoring = false
22
+
23
+ elsif compo.has_undef?(name, false)
24
+ info = compo.get_undef(name, false)
25
+ compo.remove_undef(name, false)
26
+ ::Rtype.send(:redefine_method_to_typed, self, name, info[:expected], info[:result], false)
27
+ end
28
+ end
29
+ end
30
+
31
+ def singleton_method_added(name)
32
+ if @_rtype_component
33
+ compo = @_rtype_component
34
+
35
+ if compo.annotation_mode && !compo.ignoring
36
+ if compo.has_undef?(name, true)
37
+ compo.remove_undef(name, true)
38
+ end
39
+
40
+ compo.ignoring = true
41
+ ::Rtype::define_typed_method(self, name, compo.annotation_type_sig, true)
42
+ compo.annotation_mode = false
43
+ compo.annotation_type_sig = nil
44
+ compo.ignoring = false
45
+
46
+ elsif compo.has_undef?(name, true)
47
+ info = compo.get_undef(name, true)
48
+ compo.remove_undef(name, true)
49
+ ::Rtype.send(:redefine_method_to_typed, self, name, info[:expected], info[:result], true)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,4 @@
1
+ module Rtype
2
+ class ReturnTypeError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,49 @@
1
+ module Rtype
2
+ class RtypeComponent
3
+ attr_accessor :annotation_mode, :annotation_type_sig, :ignoring
4
+ attr_reader :undef_methods, :old_methods
5
+
6
+ def initialize
7
+ @annotation_mode = false
8
+ @annotation_type_sig = nil
9
+ @ignoring = false
10
+ @undef_methods = {}
11
+ @old_methods = {}
12
+ end
13
+
14
+ def set_old(name, singleton, method)
15
+ @old_methods[singleton] ||= {}
16
+ @old_methods[singleton][name] = method
17
+ end
18
+
19
+ def get_old(name, singleton)
20
+ @old_methods[singleton][name]
21
+ end
22
+
23
+ def has_old?(name, singleton)
24
+ @old_methods.key?(singleton) && @old_methods[singleton].key?(name)
25
+ end
26
+
27
+ # @param [Symbol] name
28
+ # @param [Array] expected_args
29
+ # @param return_sig
30
+ # @param [Boolean] singleton
31
+ def add_undef(name, expected_args, return_sig, singleton)
32
+ obj = { expected: expected_args, result: return_sig }
33
+ @undef_methods[singleton] ||= {}
34
+ @undef_methods[singleton][name] = obj
35
+ end
36
+
37
+ def has_undef?(name, singleton)
38
+ @undef_methods.key?(singleton) && @undef_methods[singleton].key?(name)
39
+ end
40
+
41
+ def remove_undef(name, singleton)
42
+ @undef_methods[singleton].delete(name)
43
+ end
44
+
45
+ def get_undef(name, singleton)
46
+ @undef_methods[singleton][name]
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,10 @@
1
+ module Rtype
2
+ class TypeSignature
3
+ attr_accessor :argument_type, :return_type
4
+
5
+ # @return [Hash] A type signature
6
+ def info
7
+ {argument_type => return_type}
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module Rtype
2
+ class TypeSignatureError < ArgumentError
3
+ end
4
+ end