arg_scanner 0.2.0 → 0.3.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 +5 -5
- data/README.md +6 -1
- data/bin/arg-scanner +17 -17
- data/bin/rubymine-type-tracker +73 -0
- data/ext/arg_scanner/arg_scanner.c +503 -212
- data/ext/arg_scanner/extconf.rb +4 -2
- data/lib/arg_scanner.rb +0 -1
- data/lib/arg_scanner/options.rb +8 -8
- data/lib/arg_scanner/starter.rb +3 -6
- data/lib/arg_scanner/state_tracker.rb +29 -59
- data/lib/arg_scanner/type_tracker.rb +13 -83
- data/lib/arg_scanner/version.rb +1 -1
- data/lib/arg_scanner/workspace.rb +28 -0
- metadata +6 -5
- data/lib/arg_scanner/return_type_tracker.rb +0 -25
data/ext/arg_scanner/extconf.rb
CHANGED
@@ -22,10 +22,12 @@ config_file = File.join(File.dirname(__FILE__), 'config_options.rb')
|
|
22
22
|
load config_file if File.exist?(config_file)
|
23
23
|
|
24
24
|
if ENV['debase_debug']
|
25
|
-
$CFLAGS+=' -Wall -Werror'
|
26
|
-
$CFLAGS+=' -g3'
|
25
|
+
$CFLAGS+=' -Wall -Werror -g3'
|
27
26
|
end
|
28
27
|
|
28
|
+
$CFLAGS += ' `pkg-config --cflags --libs glib-2.0`'
|
29
|
+
$DLDFLAGS += ' `pkg-config --cflags --libs glib-2.0`'
|
30
|
+
|
29
31
|
dir_config("ruby")
|
30
32
|
if !Debase::RubyCoreSource.create_makefile_with_core(hdrs, "arg_scanner/arg_scanner")
|
31
33
|
STDERR.print("Makefile creation failed\n")
|
data/lib/arg_scanner.rb
CHANGED
data/lib/arg_scanner/options.rb
CHANGED
@@ -2,22 +2,22 @@ require 'ostruct'
|
|
2
2
|
|
3
3
|
module ArgScanner
|
4
4
|
OPTIONS = OpenStruct.new(
|
5
|
-
:local_version => ENV['ARG_SCANNER_LOCAL_VERSION'] || '0',
|
6
|
-
:no_local => ENV['ARG_SCANNER_NO_LOCAL'] ? true : false,
|
7
|
-
:project_roots => ((ENV['ARG_SCANNER_PROJECT_ROOTS'] || "").split ':'),
|
8
5
|
:enable_type_tracker => ENV['ARG_SCANNER_ENABLE_TYPE_TRACKER'],
|
9
6
|
:enable_state_tracker => ENV['ARG_SCANNER_ENABLE_STATE_TRACKER'],
|
10
|
-
:enable_return_type_tracker => ENV['ARG_SCANNER_ENABLE_RETURN_TYPE_TRACKER'],
|
11
7
|
:output_directory => ENV['ARG_SCANNER_DIR'],
|
8
|
+
:catch_only_every_n_call => ENV['ARG_SCANNER_CATCH_ONLY_EVERY_N_CALL'] || 1,
|
9
|
+
:project_root => ENV['ARG_SCANNER_PROJECT_ROOT'],
|
10
|
+
:pipe_file_path => ENV['ARG_SCANNER_PIPE_FILE_PATH'] || '',
|
11
|
+
:buffering => ENV['ARG_SCANNER_BUFFERING']
|
12
12
|
)
|
13
13
|
|
14
14
|
def OPTIONS.set_env
|
15
|
-
ENV['ARG_SCANNER_LOCAL_VERSION'] = self.local_version.to_s
|
16
|
-
ENV['ARG_SCANNER_NO_LOCAL'] = self.no_local ? "1" : nil
|
17
|
-
ENV['ARG_SCANNER_PROJECT_ROOTS'] = self.project_roots.join ':'
|
18
15
|
ENV['ARG_SCANNER_ENABLE_TYPE_TRACKER'] = self.enable_type_tracker ? "1" : nil
|
19
16
|
ENV['ARG_SCANNER_ENABLE_STATE_TRACKER'] = self.enable_state_tracker ? "1" : nil
|
20
|
-
ENV['ARG_SCANNER_ENABLE_RETURN_TYPE_TRACKER'] = self.enable_return_type_tracker ? "1" : nil
|
21
17
|
ENV['ARG_SCANNER_DIR'] = self.output_directory
|
18
|
+
ENV['ARG_SCANNER_CATCH_ONLY_EVERY_N_CALL'] = self.catch_only_every_n_call.to_s
|
19
|
+
ENV['ARG_SCANNER_PROJECT_ROOT'] = self.project_root
|
20
|
+
ENV['ARG_SCANNER_PIPE_FILE_PATH'] = self.pipe_file_path
|
21
|
+
ENV['ARG_SCANNER_BUFFERING'] = self.buffering ? "1" : nil
|
22
22
|
end
|
23
23
|
end
|
data/lib/arg_scanner/starter.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
+
# starter.rb is loaded with "ruby -r" option from bin/arg-scanner
|
2
|
+
# or by IDEA also with "ruby -r" option
|
3
|
+
|
1
4
|
unless ENV["ARG_SCANNER_ENABLE_STATE_TRACKER"].nil?
|
2
5
|
require_relative 'state_tracker'
|
3
6
|
ArgScanner::StateTracker.new
|
4
7
|
end
|
5
8
|
|
6
|
-
unless ENV["ARG_SCANNER_ENABLE_RETURN_TYPE_TRACKER"].nil?
|
7
|
-
require_relative 'return_type_tracker'
|
8
|
-
ArgScanner::ReturnTypeTracker.new
|
9
|
-
end
|
10
|
-
|
11
|
-
|
12
9
|
unless ENV["ARG_SCANNER_ENABLE_TYPE_TRACKER"].nil?
|
13
10
|
require_relative 'arg_scanner'
|
14
11
|
require_relative 'type_tracker'
|
@@ -1,28 +1,35 @@
|
|
1
1
|
require "set"
|
2
2
|
require_relative "require_all"
|
3
|
+
require_relative "workspace"
|
3
4
|
|
4
5
|
|
5
6
|
module ArgScanner
|
6
7
|
class StateTracker
|
7
8
|
def initialize
|
9
|
+
@workspace = Workspace.new
|
10
|
+
@workspace.on_process_start
|
8
11
|
at_exit do
|
9
|
-
dir = ENV["ARG_SCANNER_DIR"]
|
10
|
-
dir = "." if dir.nil? || dir == ""
|
11
|
-
path = dir + "/" + "classes-#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}-#{Process.pid}.json"
|
12
12
|
begin
|
13
|
-
|
14
|
-
|
13
|
+
require_extra_libs
|
14
|
+
@workspace.open_output_json("classes") { |file| print_json(file) }
|
15
|
+
ensure
|
16
|
+
@workspace.on_process_exit
|
15
17
|
end
|
16
|
-
begin
|
17
|
-
Rails.application.eager_load!
|
18
|
-
rescue => e
|
19
|
-
end
|
20
|
-
|
21
|
-
File.open(path,"w") { |file| print_json(file) }
|
22
18
|
end
|
23
19
|
end
|
24
20
|
|
25
21
|
private
|
22
|
+
def require_extra_libs
|
23
|
+
begin
|
24
|
+
RequireAll.require_all Rails.root.join('lib')
|
25
|
+
rescue Exception => e
|
26
|
+
end
|
27
|
+
begin
|
28
|
+
Rails.application.eager_load!
|
29
|
+
rescue Exception => e
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
26
33
|
def print_json(file)
|
27
34
|
result = {
|
28
35
|
:top_level_constants => parse_top_level_constants,
|
@@ -47,49 +54,7 @@ module ArgScanner
|
|
47
54
|
end
|
48
55
|
|
49
56
|
def get_extra_methods(value)
|
50
|
-
|
51
|
-
(value.methods - value.class.public_instance_methods).map do |method_name|
|
52
|
-
method = value.public_method(method_name)
|
53
|
-
method.owner
|
54
|
-
end.uniq
|
55
|
-
rescue Exception => e
|
56
|
-
value.methods - value.class.instance_methods
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
def get_constants_of_class(constants, parent, klass)
|
62
|
-
constants.select {|const| parent.const_defined?(const)}.map do |const|
|
63
|
-
begin
|
64
|
-
parent.const_get(const)
|
65
|
-
rescue Exception => e
|
66
|
-
end
|
67
|
-
end.select { |const| const.is_a? klass}
|
68
|
-
rescue => e
|
69
|
-
$stderr.puts(e)
|
70
|
-
[]
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
def get_modules(mod)
|
75
|
-
get_constants_of_class(mod.constants, mod, Module)
|
76
|
-
end
|
77
|
-
|
78
|
-
def get_all_modules
|
79
|
-
queue = Queue.new
|
80
|
-
visited = Set.new
|
81
|
-
get_modules(Module).each {|mod| queue.push(mod); visited.add(mod)}
|
82
|
-
|
83
|
-
until queue.empty? do
|
84
|
-
mod = queue.pop
|
85
|
-
get_modules(mod).each do |child|
|
86
|
-
unless visited.include?(child)
|
87
|
-
queue.push(child)
|
88
|
-
visited.add(child)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
visited
|
57
|
+
value.methods - value.public_methods
|
93
58
|
end
|
94
59
|
|
95
60
|
def method_to_json(method)
|
@@ -102,23 +67,28 @@ module ArgScanner
|
|
102
67
|
ret[:line] = method.source_location[1]
|
103
68
|
end
|
104
69
|
ret
|
70
|
+
rescue Exception => e
|
71
|
+
nil
|
105
72
|
end
|
106
73
|
|
107
74
|
def module_to_json(mod)
|
108
75
|
ret = {
|
109
76
|
:name => mod.to_s,
|
110
77
|
:type => mod.class.to_s,
|
111
|
-
:
|
112
|
-
:
|
113
|
-
|
114
|
-
:
|
78
|
+
:singleton_class_ancestors => mod.singleton_class.ancestors.map{|it| it.to_s},
|
79
|
+
:ancestors => mod.ancestors.map{|it| it.to_s}, # map to_s is needed because for example "Psych" parsed not correctly into JSON format
|
80
|
+
# it's parsed as: "{}\n" check it by launching in rails console: "JSON.generate(Psych)"
|
81
|
+
:class_methods => mod.methods(false).map {|method| method_to_json(mod.method(method))}.compact,
|
82
|
+
:instance_methods => mod.instance_methods(false).map {|method| method_to_json(mod.instance_method(method))}.compact
|
115
83
|
}
|
116
84
|
ret[:superclass] = mod.superclass if mod.is_a? Class
|
117
85
|
ret
|
86
|
+
rescue Exception => e
|
87
|
+
nil
|
118
88
|
end
|
119
89
|
|
120
90
|
def modules_to_json
|
121
|
-
|
91
|
+
ObjectSpace.each_object(Module).map {|mod| module_to_json(mod)}
|
122
92
|
end
|
123
93
|
end
|
124
94
|
end
|
@@ -51,99 +51,29 @@ module ArgScanner
|
|
51
51
|
class TypeTracker
|
52
52
|
include Singleton
|
53
53
|
|
54
|
-
GEM_PATH_REVERSED_REGEX = /((?:[0-9A-Za-z]+\.)+\d+)-([A-Za-z0-9_-]+)/
|
55
|
-
|
56
54
|
def initialize
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@prefix = ENV["ARG_SCANNER_PREFIX"]
|
55
|
+
ArgScanner.init(ENV['ARG_SCANNER_PIPE_FILE_PATH'], ENV['ARG_SCANNER_BUFFERING'],
|
56
|
+
ENV['ARG_SCANNER_PROJECT_ROOT'], ENV['ARG_SCANNER_CATCH_ONLY_EVERY_N_CALL'])
|
57
|
+
|
61
58
|
@enable_debug = ENV["ARG_SCANNER_DEBUG"]
|
62
59
|
@performance_monitor = if @enable_debug then TypeTrackerPerformanceMonitor.new else nil end
|
63
|
-
TracePoint.trace(:call, :return) do |tp|
|
64
|
-
case tp.event
|
65
|
-
when :call
|
66
|
-
handle_call(tp)
|
67
|
-
when :return
|
68
|
-
handle_return(tp)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
attr_accessor :enable_debug
|
74
|
-
attr_accessor :performance_monitor
|
75
|
-
attr_accessor :cache
|
76
|
-
attr_accessor :socket
|
77
|
-
attr_accessor :mutex
|
78
|
-
attr_accessor :prefix
|
79
60
|
|
61
|
+
TracePoint.trace(:call, &ArgScanner.method(:handle_call))
|
80
62
|
|
63
|
+
TracePoint.trace(:return, &ArgScanner.method(:handle_return))
|
81
64
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
65
|
+
error_msg = ArgScanner.check_if_arg_scanner_ready()
|
66
|
+
if error_msg != nil
|
67
|
+
STDERR.puts error_msg
|
68
|
+
Process.exit(1)
|
86
69
|
end
|
87
70
|
|
88
|
-
|
89
|
-
return ['', ''] unless GEM_PATH_REVERSED_REGEX =~ reversed
|
90
|
-
|
91
|
-
name_and_version = Regexp.last_match
|
92
|
-
return name_and_version[2].reverse, name_and_version[1].reverse
|
93
|
-
end
|
94
|
-
|
95
|
-
def signatures
|
96
|
-
Thread.current[:signatures] ||= Array.new
|
97
|
-
end
|
98
|
-
|
99
|
-
def at_exit
|
100
|
-
socket.close
|
101
|
-
end
|
102
|
-
|
103
|
-
def put_to_socket(message)
|
104
|
-
mutex.synchronize { socket.puts(message) }
|
71
|
+
ObjectSpace.define_finalizer(self, proc { ArgScanner.destructor() })
|
105
72
|
end
|
106
73
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
if prefix.nil? || tp.path.start_with?(prefix)
|
111
|
-
performance_monitor.on_call unless performance_monitor.nil?
|
112
|
-
signature = ArgScanner.handle_call(tp.lineno, tp.method_id, tp.path)
|
113
|
-
signatures.push(signature)
|
114
|
-
else
|
115
|
-
signatures.push(false)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def handle_return(tp)
|
120
|
-
sigi = signatures
|
121
|
-
performance_monitor.on_return unless performance_monitor.nil?
|
122
|
-
|
123
|
-
unless sigi.empty?
|
124
|
-
signature = sigi.pop
|
125
|
-
return unless signature
|
126
|
-
|
127
|
-
performance_monitor.on_handled_return unless performance_monitor.nil?
|
128
|
-
|
129
|
-
defined_class = tp.defined_class
|
130
|
-
return if !defined_class || defined_class.singleton_class?
|
131
|
-
|
132
|
-
receiver_name = defined_class.name ? defined_class : defined_class.ancestors.first
|
133
|
-
return_type_name = tp.return_value.class
|
134
|
-
|
135
|
-
return if !receiver_name || !receiver_name.to_s || receiver_name.to_s.length > 200
|
136
|
-
|
137
|
-
json = ArgScanner.handle_return(signature, return_type_name) +
|
138
|
-
"\"receiver_name\":\"#{receiver_name}\",\"return_type_name\":\"#{return_type_name}\","
|
139
|
-
|
140
|
-
if cache.add?(json)
|
141
|
-
gem_name, gem_version = TypeTracker.extract_gem_name_and_version(tp.path)
|
142
|
-
json += '"gem_name":"' + gem_name.to_s + '","gem_version":"' + gem_version.to_s + '"}'
|
143
|
-
put_to_socket(json)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
74
|
+
attr_accessor :enable_debug
|
75
|
+
attr_accessor :performance_monitor
|
76
|
+
attr_accessor :prefix
|
147
77
|
|
148
78
|
end
|
149
79
|
end
|
data/lib/arg_scanner/version.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
module ArgScanner
|
2
|
+
class Workspace
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@dir = ENV["ARG_SCANNER_DIR"] || "."
|
6
|
+
@pid_file = @dir+"/#{Process.pid}.pid"
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_process_start
|
10
|
+
File.open(@pid_file, "w") {}
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def open_output_json(prefix)
|
15
|
+
path = @dir + "/#{prefix}-#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}-#{Process.pid}.json"
|
16
|
+
path_tmp_name = path + ".temp"
|
17
|
+
File.open(path_tmp_name, "w") { |file| yield file }
|
18
|
+
require 'fileutils'
|
19
|
+
FileUtils.mv(path_tmp_name, path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_process_exit
|
23
|
+
require 'fileutils'
|
24
|
+
FileUtils.rm(@pid_file)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arg_scanner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nickolay Viuginov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2019-02-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -76,6 +76,7 @@ email:
|
|
76
76
|
executables:
|
77
77
|
- arg-scanner
|
78
78
|
- console
|
79
|
+
- rubymine-type-tracker
|
79
80
|
- setup
|
80
81
|
extensions:
|
81
82
|
- ext/arg_scanner/extconf.rb
|
@@ -87,9 +88,9 @@ files:
|
|
87
88
|
- README.md
|
88
89
|
- Rakefile
|
89
90
|
- arg_scanner.gemspec
|
90
|
-
- arg_scanner.iml
|
91
91
|
- bin/arg-scanner
|
92
92
|
- bin/console
|
93
|
+
- bin/rubymine-type-tracker
|
93
94
|
- bin/setup
|
94
95
|
- ext/arg_scanner/arg_scanner.c
|
95
96
|
- ext/arg_scanner/arg_scanner.h
|
@@ -97,11 +98,11 @@ files:
|
|
97
98
|
- lib/arg_scanner.rb
|
98
99
|
- lib/arg_scanner/options.rb
|
99
100
|
- lib/arg_scanner/require_all.rb
|
100
|
-
- lib/arg_scanner/return_type_tracker.rb
|
101
101
|
- lib/arg_scanner/starter.rb
|
102
102
|
- lib/arg_scanner/state_tracker.rb
|
103
103
|
- lib/arg_scanner/type_tracker.rb
|
104
104
|
- lib/arg_scanner/version.rb
|
105
|
+
- lib/arg_scanner/workspace.rb
|
105
106
|
- util/state_filter.rb
|
106
107
|
homepage: https://github.com/jetbrains/ruby-type-inference
|
107
108
|
licenses:
|
@@ -123,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
124
|
version: '0'
|
124
125
|
requirements: []
|
125
126
|
rubyforge_project:
|
126
|
-
rubygems_version: 2.
|
127
|
+
rubygems_version: 2.7.7
|
127
128
|
signing_key:
|
128
129
|
specification_version: 4
|
129
130
|
summary: Program execution tracker to retrieve data types information
|
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
|
-
module ArgScanner
|
4
|
-
class ReturnTypeTracker
|
5
|
-
def initialize
|
6
|
-
@result = Set.new
|
7
|
-
TracePoint.new(:return) do |tp|
|
8
|
-
@result.add( {
|
9
|
-
def: tp.defined_class,
|
10
|
-
name: tp.method_id,
|
11
|
-
ret: tp.return_value.class
|
12
|
-
})
|
13
|
-
end.enable
|
14
|
-
at_exit do
|
15
|
-
dir = ENV["ARG_SCANNER_DIR"]
|
16
|
-
dir = "." if dir.nil? || dir == ""
|
17
|
-
path = dir + "/calls-#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}-#{Process.pid}.json"
|
18
|
-
require 'json'
|
19
|
-
File.open(path,"w") { |file| file.puts(JSON.dump(@result.to_a)) }
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
|