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.
@@ -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
-