delfos 0.0.1.pre.rc1 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/delfos.rb +22 -78
- data/lib/delfos/call_stack.rb +39 -0
- data/lib/delfos/call_stack/stack.rb +59 -0
- data/lib/delfos/file_system/common_path.rb +62 -0
- data/lib/delfos/{distance/calculation.rb → file_system/distance_calculation.rb} +4 -15
- data/lib/delfos/file_system/path_determination.rb +37 -0
- data/lib/delfos/{distance → file_system}/relation.rb +1 -1
- data/lib/delfos/method_logging.rb +32 -49
- data/lib/delfos/method_logging/call_site_parsing.rb +74 -0
- data/lib/delfos/method_logging/code_location.rb +34 -98
- data/lib/delfos/method_logging/{args.rb → method_parameters.rb} +17 -18
- data/lib/delfos/neo4j.rb +58 -0
- data/lib/delfos/neo4j/batch/execution.rb +149 -0
- data/lib/delfos/neo4j/{execution_persistence.rb → call_stack_query.rb} +12 -22
- data/lib/delfos/neo4j/distance/call_site_fetcher.rb +19 -0
- data/lib/delfos/neo4j/distance/update.rb +50 -0
- data/lib/delfos/neo4j/informer.rb +62 -34
- data/lib/delfos/neo4j/query_execution/errors.rb +30 -0
- data/lib/delfos/neo4j/query_execution/http.rb +61 -0
- data/lib/delfos/neo4j/query_execution/http_query.rb +67 -0
- data/lib/delfos/neo4j/query_execution/sync.rb +35 -0
- data/lib/delfos/neo4j/query_execution/transactional.rb +68 -0
- data/lib/delfos/neo4j/schema.rb +73 -0
- data/lib/delfos/patching/basic_object.rb +1 -5
- data/lib/delfos/patching/method_cache.rb +83 -0
- data/lib/delfos/patching/method_override.rb +76 -66
- data/lib/delfos/patching/module_defining_methods.rb +105 -0
- data/lib/delfos/patching/unstubber.rb +34 -0
- data/lib/delfos/setup.rb +88 -0
- metadata +42 -15
- data/lib/delfos/common_path.rb +0 -58
- data/lib/delfos/execution_chain.rb +0 -75
- data/lib/delfos/method_logging/added_methods.rb +0 -67
- data/lib/delfos/neo4j/distance_update.rb +0 -73
- data/lib/delfos/neo4j/query_execution.rb +0 -79
- data/lib/delfos/patching/unstubbing_spec_helper.rb +0 -58
@@ -1,29 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require_relative "query_execution"
|
3
|
-
|
4
2
|
module Delfos
|
5
3
|
module Neo4j
|
6
|
-
class
|
7
|
-
def self.save!(chain)
|
8
|
-
new(chain.call_sites, chain.execution_count).save!
|
9
|
-
end
|
10
|
-
|
11
|
-
def save!
|
12
|
-
Neo4j::QueryExecution.execute(query, params)
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
4
|
+
class CallStackQuery
|
17
5
|
def initialize(call_sites, execution_count)
|
18
6
|
@call_sites = call_sites
|
19
7
|
|
20
8
|
@execution_count = execution_count
|
21
9
|
end
|
22
10
|
|
23
|
-
attr_reader :call_sites, :execution_count
|
24
|
-
|
25
11
|
def query
|
26
|
-
map_call_sites do |c,i|
|
12
|
+
map_call_sites do |c, i|
|
27
13
|
call_site_query(c, i)
|
28
14
|
end.join("\n")
|
29
15
|
end
|
@@ -31,21 +17,25 @@ module Delfos
|
|
31
17
|
def params
|
32
18
|
params = {}
|
33
19
|
|
34
|
-
map_call_sites do |c,i|
|
20
|
+
map_call_sites do |c, i|
|
35
21
|
params.merge!(call_site_params(c, i))
|
36
22
|
end
|
37
23
|
|
38
24
|
params
|
39
25
|
end
|
40
26
|
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :call_sites, :execution_count
|
30
|
+
|
41
31
|
def map_call_sites
|
42
32
|
call_sites.compact.map.with_index do |c, i|
|
43
33
|
yield c, i
|
44
34
|
end
|
45
35
|
end
|
46
36
|
|
47
|
-
def call_site_query(
|
48
|
-
|
37
|
+
def call_site_query(_cs, i)
|
38
|
+
<<-QUERY
|
49
39
|
MERGE
|
50
40
|
|
51
41
|
(
|
@@ -70,7 +60,7 @@ module Delfos
|
|
70
60
|
|
71
61
|
(cs#{i}:CallSite {file: {file#{i}}, line_number: {line_number#{i}}})
|
72
62
|
|
73
|
-
MERGE (e#{i}:
|
63
|
+
MERGE (e#{i}:CallStack{number: {execution_count#{i}}})
|
74
64
|
|
75
65
|
MERGE (e#{i})
|
76
66
|
-
|
@@ -82,7 +72,7 @@ module Delfos
|
|
82
72
|
|
83
73
|
def call_site_params(cs, i)
|
84
74
|
{
|
85
|
-
"klass#{i}" => cs.klass,
|
75
|
+
"klass#{i}" => cs.klass.to_s,
|
86
76
|
"method_name#{i}" => cs.method_name,
|
87
77
|
"method_type#{i}" => cs.method_type,
|
88
78
|
"method_definition_file#{i}" => cs.method_definition_file,
|
@@ -90,7 +80,7 @@ module Delfos
|
|
90
80
|
"method_definition_line#{i}" => cs.method_definition_line,
|
91
81
|
"file#{i}" => cs.file,
|
92
82
|
"line_number#{i}" => cs.line_number,
|
93
|
-
"step_number#{i}" => i + 1
|
83
|
+
"step_number#{i}" => i + 1,
|
94
84
|
}
|
95
85
|
end
|
96
86
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Delfos
|
3
|
+
module Neo4j
|
4
|
+
module Distance
|
5
|
+
class CallSiteFetcher
|
6
|
+
def self.perform
|
7
|
+
Neo4j.execute_sync <<-QUERY
|
8
|
+
MATCH
|
9
|
+
(call_site:CallSite) - [:CALLS] -> (called:Method)
|
10
|
+
|
11
|
+
RETURN
|
12
|
+
call_site.file, id(call_site),
|
13
|
+
called.file, id(called)
|
14
|
+
QUERY
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "delfos/file_system/distance_calculation"
|
3
|
+
require_relative "call_site_fetcher"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module Neo4j
|
7
|
+
module Distance
|
8
|
+
class Update
|
9
|
+
def perform
|
10
|
+
results = CallSiteFetcher.perform
|
11
|
+
return if results.length.negative?
|
12
|
+
|
13
|
+
results.each do |start_file, call_site_id, finish_file, called_id|
|
14
|
+
calc = FileSystem::DistanceCalculation.new(start_file, finish_file)
|
15
|
+
|
16
|
+
update(call_site_id, called_id, calc)
|
17
|
+
end
|
18
|
+
|
19
|
+
Neo4j.flush!
|
20
|
+
end
|
21
|
+
|
22
|
+
def update(call_site_id, called_id, calc)
|
23
|
+
Neo4j.execute(
|
24
|
+
query,
|
25
|
+
call_site_id: call_site_id,
|
26
|
+
called_id: called_id,
|
27
|
+
sum_traversals: calc.sum_traversals,
|
28
|
+
sum_possible_traversals: calc.sum_possible_traversals
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def query
|
33
|
+
<<-QUERY
|
34
|
+
START call_site = node({call_site_id}),
|
35
|
+
called = node({called_id})
|
36
|
+
|
37
|
+
MERGE (call_site)
|
38
|
+
-
|
39
|
+
[:EFFERENT_COUPLING {
|
40
|
+
distance: {sum_traversals},
|
41
|
+
possible_distance: {sum_possible_traversals}
|
42
|
+
}
|
43
|
+
]
|
44
|
+
-> (called)
|
45
|
+
QUERY
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,69 +1,101 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "delfos"
|
3
|
-
require_relative "query_execution"
|
4
2
|
|
5
3
|
module Delfos
|
6
4
|
module Neo4j
|
7
5
|
class Informer
|
8
|
-
def
|
9
|
-
|
6
|
+
def save_call_stack(call_sites, execution_number)
|
7
|
+
q = Neo4j::CallStackQuery.new(call_sites, execution_number)
|
8
|
+
Neo4j.execute(q.query, q.params)
|
10
9
|
end
|
11
10
|
|
12
|
-
def
|
13
|
-
query = query_for(
|
14
|
-
params =
|
11
|
+
def log(args, call_site, called_code)
|
12
|
+
query = query_for(args, call_site, called_code)
|
13
|
+
params = params_for(args, call_site, called_code)
|
15
14
|
|
16
|
-
|
15
|
+
Neo4j.execute(query, params)
|
16
|
+
end
|
17
|
+
|
18
|
+
def params_for(args, call_site, called_code)
|
19
|
+
assign_query_variables(args, call_site, called_code)
|
20
|
+
|
21
|
+
params = query_variables.each_with_object({}) do |(klass, name), object|
|
22
|
+
object[name] = klass.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
params["m1_type"] = call_site.method_type
|
26
|
+
params["m1_name"] = call_site.method_name
|
27
|
+
params["m1_file"] = call_site.method_definition_file
|
28
|
+
params["m1_line_number"] = call_site.method_definition_line
|
29
|
+
|
30
|
+
params["cs_file"] = call_site.file
|
31
|
+
params["cs_line_number"] = call_site.line_number
|
32
|
+
|
33
|
+
params["m2_type"] = called_code.method_type
|
34
|
+
params["m2_name"] = called_code.method_name
|
35
|
+
params["m2_file"] = called_code.method_definition_file
|
36
|
+
params["m2_line_number"] = called_code.method_definition_line
|
37
|
+
|
38
|
+
params
|
17
39
|
end
|
18
40
|
|
19
41
|
def query_for(args, call_site, called_code)
|
20
42
|
assign_query_variables(args, call_site, called_code)
|
21
43
|
|
22
|
-
klasses_query = query_variables.map do |
|
23
|
-
"MERGE (#{name}:Class {name: #{
|
44
|
+
klasses_query = query_variables.map do |_klass, name|
|
45
|
+
"MERGE (#{name}:Class {name: {#{name}}})"
|
24
46
|
end.join("\n")
|
25
47
|
|
26
48
|
<<-QUERY
|
27
49
|
#{klasses_query}
|
28
50
|
|
29
|
-
MERGE (#{query_variable(call_site.klass)}) - [:OWNS] ->
|
51
|
+
MERGE (#{query_variable(call_site.klass)}) - [:OWNS] ->
|
52
|
+
#{method_node("m1")}
|
30
53
|
|
31
|
-
MERGE (m1) - [:CONTAINS] ->
|
54
|
+
MERGE (m1) - [:CONTAINS] ->
|
55
|
+
(cs:CallSite
|
56
|
+
{
|
57
|
+
file: {cs_file},
|
58
|
+
line_number: {cs_line_number}
|
59
|
+
}
|
60
|
+
)
|
32
61
|
|
33
|
-
MERGE (#{query_variable(called_code.klass)}) - [:OWNS] ->
|
62
|
+
MERGE (#{query_variable(called_code.klass)}) - [:OWNS] ->
|
63
|
+
#{method_node("m2")}
|
34
64
|
|
35
|
-
MERGE (cs)
|
65
|
+
MERGE (cs) - [:CALLS] -> (m2)
|
36
66
|
|
37
67
|
#{args_query args}
|
38
68
|
QUERY
|
39
69
|
end
|
40
70
|
|
41
71
|
def assign_query_variables(args, call_site, called_code)
|
42
|
-
|
43
|
-
query_variables.assign(called_code.klass, "k")
|
72
|
+
klasses = [call_site.klass, called_code.klass] + args.argument_classes
|
44
73
|
|
45
|
-
|
74
|
+
klasses.uniq.each do |k|
|
46
75
|
query_variables.assign(k, "k")
|
47
76
|
end
|
48
77
|
end
|
49
78
|
|
50
|
-
def method_node(
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
79
|
+
def method_node(id)
|
80
|
+
<<-NODE
|
81
|
+
(#{id}:Method
|
82
|
+
{
|
83
|
+
type: {#{id}_type},
|
84
|
+
name: {#{id}_name},
|
85
|
+
file: {#{id}_file},
|
86
|
+
line_number: {#{id}_line_number}
|
87
|
+
}
|
88
|
+
)
|
89
|
+
NODE
|
60
90
|
end
|
61
91
|
|
62
92
|
def args_query(args)
|
63
|
-
|
93
|
+
query_text = args.argument_classes.map do |k|
|
64
94
|
name = query_variable(k)
|
65
95
|
"MERGE (cs) - [:ARG] -> (#{name})"
|
66
|
-
end
|
96
|
+
end
|
97
|
+
|
98
|
+
query_text.join("\n")
|
67
99
|
end
|
68
100
|
|
69
101
|
def query_variable(k)
|
@@ -74,10 +106,6 @@ module Delfos
|
|
74
106
|
@query_variables ||= QueryVariables.new
|
75
107
|
end
|
76
108
|
|
77
|
-
def code_execution_query
|
78
|
-
Delfos::Patching.execution_chain
|
79
|
-
end
|
80
|
-
|
81
109
|
class QueryVariables < Hash
|
82
110
|
def initialize(*args)
|
83
111
|
super(*args)
|
@@ -85,7 +113,7 @@ module Delfos
|
|
85
113
|
end
|
86
114
|
|
87
115
|
def assign(klass, prefix)
|
88
|
-
klass = klass.to_s
|
116
|
+
klass = klass.to_s
|
89
117
|
val = self[klass]
|
90
118
|
return val if val
|
91
119
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Delfos
|
3
|
+
module Neo4j
|
4
|
+
module QueryExecution
|
5
|
+
HTTP_ERRORS = [
|
6
|
+
EOFError,
|
7
|
+
Errno::ECONNRESET,
|
8
|
+
Errno::ECONNREFUSED,
|
9
|
+
Errno::EINVAL,
|
10
|
+
Net::HTTPBadResponse,
|
11
|
+
Net::HTTPHeaderSyntaxError,
|
12
|
+
Net::ProtocolError,
|
13
|
+
Timeout::Error,
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
class InvalidQuery < IOError
|
17
|
+
def initialize(errors, query, params)
|
18
|
+
message = errors.map do |e|
|
19
|
+
e.select { |k, _| %w(code message).include?(k) }.inspect
|
20
|
+
end.join("\n")
|
21
|
+
|
22
|
+
super [message, { query: query, params: params }.to_json].join("\n\n")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ConnectionError < IOError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "delfos"
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module Neo4j
|
7
|
+
module QueryExecution
|
8
|
+
class Http
|
9
|
+
attr_reader :uri
|
10
|
+
|
11
|
+
def initialize(uri)
|
12
|
+
@uri = uri
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(body)
|
16
|
+
response(build_post(body))
|
17
|
+
end
|
18
|
+
|
19
|
+
def get
|
20
|
+
response(build_get)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def response(request)
|
26
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
27
|
+
|
28
|
+
http.request(request)
|
29
|
+
rescue *HTTP_ERRORS => e
|
30
|
+
raise ConnectionError, e
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_post(body)
|
34
|
+
build_request("Post") do |request|
|
35
|
+
add_headers(request)
|
36
|
+
|
37
|
+
request.body = body
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_get
|
42
|
+
build_request("Get") do |request|
|
43
|
+
add_headers(request)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_request(type)
|
48
|
+
Net::HTTP.const_get(type).new(uri.request_uri).tap do |request|
|
49
|
+
yield request
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_headers(request)
|
54
|
+
request.initialize_http_header("Accept" => "application/json; charset=UTF-8")
|
55
|
+
request.basic_auth(Delfos.neo4j.username, Delfos.neo4j.password)
|
56
|
+
request.content_type = "application/json"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Delfos
|
3
|
+
module Neo4j
|
4
|
+
module QueryExecution
|
5
|
+
module HttpQuery
|
6
|
+
def self.included(base)
|
7
|
+
base.instance_eval do
|
8
|
+
attr_reader :query, :params
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(query, params, uri = nil)
|
13
|
+
@query = query
|
14
|
+
@params = params
|
15
|
+
@uri = uri
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def request_body
|
21
|
+
{
|
22
|
+
"statements": [{ "statement": strip_whitespace(query), "parameters": params }],
|
23
|
+
}.to_json
|
24
|
+
end
|
25
|
+
|
26
|
+
def strip_whitespace(s)
|
27
|
+
s.
|
28
|
+
gsub(/^\s+/, "").
|
29
|
+
gsub(/ +/, " ").
|
30
|
+
gsub("\n\n", "\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
def json
|
34
|
+
JSON.parse response.body
|
35
|
+
end
|
36
|
+
|
37
|
+
def errors?
|
38
|
+
json["errors"].length.positive?
|
39
|
+
end
|
40
|
+
|
41
|
+
def response
|
42
|
+
@response ||= fetch
|
43
|
+
end
|
44
|
+
|
45
|
+
def fetch
|
46
|
+
log_query
|
47
|
+
|
48
|
+
Http.new(uri).post(request_body)
|
49
|
+
end
|
50
|
+
|
51
|
+
def log_query
|
52
|
+
statement = strip_whitespace(query)
|
53
|
+
|
54
|
+
if statement
|
55
|
+
begin
|
56
|
+
params.each { |k, v| statement = statement.gsub("{#{k}}", v.inspect) }
|
57
|
+
rescue
|
58
|
+
byebug
|
59
|
+
end
|
60
|
+
Delfos.logger.debug "sending query: "
|
61
|
+
Delfos.logger.debug statement.gsub(/^/, " ")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|