arg_scanner 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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")
@@ -2,7 +2,6 @@ require "arg_scanner/version"
2
2
  require "arg_scanner/arg_scanner"
3
3
  require "arg_scanner/type_tracker"
4
4
  require "arg_scanner/state_tracker"
5
- require "arg_scanner/return_type_tracker"
6
5
 
7
6
  module ArgScanner
8
7
  # Your code goes here...
@@ -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
@@ -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
- RequireAll.require_all Rails.root.join('lib')
14
- rescue => e
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
- begin
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
- :singleton_class_included => mod.singleton_class.included_modules,
112
- :included => mod.included_modules,
113
- :class_methods => mod.methods(false).map {|method| method_to_json(mod.method(method))},
114
- :instance_methods => mod.instance_methods(false).map {|method| method_to_json(mod.instance_method(method))}
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
- get_all_modules.map {|mod| module_to_json(mod)}
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
- @cache = Set.new
58
- @socket = TCPSocket.new('127.0.0.1', 7777)
59
- @mutex = Mutex.new
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
- # @param [String] path
83
- def self.extract_gem_name_and_version(path)
84
- if OPTIONS.project_roots && path.start_with?(*OPTIONS.project_roots)
85
- return OPTIONS.no_local ? ['', ''] : ['LOCAL', OPTIONS.local_version]
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
- reversed = path.reverse
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
- private
108
- def handle_call(tp)
109
- #handle_call(VALUE self, VALUE lineno, VALUE method_name, VALUE path)
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
@@ -1,3 +1,3 @@
1
1
  module ArgScanner
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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.2.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: 2017-11-23 00:00:00.000000000 Z
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.6.11
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
-