adsl 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -20
- data/README.md +14 -21
- data/bin/adsl-verify +8 -8
- data/lib/adsl.rb +3 -0
- data/lib/adsl/adsl.rb +3 -0
- data/lib/adsl/ds/data_store_spec.rb +339 -0
- data/lib/adsl/extract/instrumenter.rb +206 -0
- data/lib/adsl/extract/meta.rb +33 -0
- data/lib/adsl/extract/rails/action_block_builder.rb +233 -0
- data/lib/adsl/extract/rails/action_instrumenter.rb +400 -0
- data/lib/adsl/extract/rails/action_runner.rb +57 -0
- data/lib/adsl/extract/rails/active_record_metaclass_generator.rb +555 -0
- data/lib/adsl/extract/rails/callback_chain_simulator.rb +135 -0
- data/lib/adsl/extract/rails/invariant_extractor.rb +48 -0
- data/lib/adsl/extract/rails/invariant_instrumenter.rb +70 -0
- data/lib/adsl/extract/rails/other_meta.rb +57 -0
- data/lib/adsl/extract/rails/rails_extractor.rb +211 -0
- data/lib/adsl/extract/rails/rails_instrumentation_test_case.rb +34 -0
- data/lib/adsl/extract/rails/rails_special_gem_instrumentation.rb +120 -0
- data/lib/adsl/extract/rails/rails_test_helper.rb +140 -0
- data/lib/adsl/extract/sexp_utils.rb +54 -0
- data/lib/adsl/fol/first_order_logic.rb +261 -0
- data/lib/adsl/parser/adsl_parser.racc +159 -0
- data/lib/{parser → adsl/parser}/adsl_parser.rex +4 -4
- data/lib/{parser → adsl/parser}/adsl_parser.rex.rb +6 -6
- data/lib/adsl/parser/adsl_parser.tab.rb +1031 -0
- data/lib/adsl/parser/ast_nodes.rb +1410 -0
- data/lib/adsl/railtie.rb +67 -0
- data/lib/adsl/spass/bin.rb +230 -0
- data/lib/{spass → adsl/spass}/ruby_extensions.rb +0 -0
- data/lib/adsl/spass/spass_ds_extensions.rb +931 -0
- data/lib/adsl/spass/spass_translator.rb +393 -0
- data/lib/adsl/spass/util.rb +13 -0
- data/lib/adsl/util/csv_hash_formatter.rb +94 -0
- data/lib/adsl/util/general.rb +228 -0
- data/lib/adsl/util/test_helper.rb +71 -0
- data/lib/adsl/verification/formula_generators.rb +231 -0
- data/lib/adsl/verification/instrumentation_filter.rb +50 -0
- data/lib/adsl/verification/invariant.rb +19 -0
- data/lib/adsl/verification/rails_verification.rb +33 -0
- data/lib/adsl/verification/utils.rb +20 -0
- data/lib/adsl/verification/verification_case.rb +13 -0
- data/test/integration/rails/rails_branch_verification_test.rb +112 -0
- data/test/integration/rails/rails_verification_test.rb +253 -0
- data/test/integration/spass/basic_translation_test.rb +563 -0
- data/test/integration/spass/control_flow_translation_test.rb +421 -0
- data/test/unit/adsl/ds/data_store_spec_test.rb +54 -0
- data/test/unit/adsl/extract/instrumenter_test.rb +103 -0
- data/test/unit/adsl/extract/meta_test.rb +142 -0
- data/test/unit/adsl/extract/rails/action_block_builder_test.rb +178 -0
- data/test/unit/adsl/extract/rails/action_instrumenter_test.rb +68 -0
- data/test/unit/adsl/extract/rails/active_record_metaclass_generator_test.rb +336 -0
- data/test/unit/adsl/extract/rails/callback_chain_simulator_test.rb +76 -0
- data/test/unit/adsl/extract/rails/invariant_extractor_test.rb +92 -0
- data/test/unit/adsl/extract/rails/rails_extractor_test.rb +1380 -0
- data/test/unit/adsl/extract/rails/rails_test_helper_test.rb +25 -0
- data/test/unit/adsl/extract/sexp_utils_test.rb +100 -0
- data/test/unit/adsl/fol/first_order_logic_test.rb +227 -0
- data/test/unit/adsl/parser/action_parser_test.rb +1040 -0
- data/test/unit/adsl/parser/ast_nodes_test.rb +359 -0
- data/test/unit/adsl/parser/class_parser_test.rb +288 -0
- data/test/unit/adsl/parser/general_parser_test.rb +67 -0
- data/test/unit/adsl/parser/invariant_parser_test.rb +432 -0
- data/test/unit/adsl/parser/parser_util_test.rb +126 -0
- data/test/unit/adsl/spass/bin_test.rb +65 -0
- data/test/unit/adsl/spass/ruby_extensions_test.rb +39 -0
- data/test/unit/adsl/spass/spass_ds_extensions_test.rb +7 -0
- data/test/unit/adsl/spass/spass_translator_test.rb +342 -0
- data/test/unit/adsl/util/csv_hash_formatter_test.rb +68 -0
- data/test/unit/adsl/util/general_test.rb +303 -0
- data/test/unit/adsl/util/test_helper_test.rb +120 -0
- data/test/unit/adsl/verification/formula_generators_test.rb +200 -0
- data/test/unit/adsl/verification/instrumentation_filter_test.rb +39 -0
- data/test/unit/adsl/verification/utils_test.rb +39 -0
- data/test/unit/adsl/verification/verification_case_test.rb +8 -0
- metadata +229 -29
- data/lib/ds/data_store_spec.rb +0 -292
- data/lib/fol/first_order_logic.rb +0 -260
- data/lib/parser/adsl_ast.rb +0 -779
- data/lib/parser/adsl_parser.racc +0 -151
- data/lib/parser/adsl_parser.tab.rb +0 -976
- data/lib/parser/dsdl_parser.rex.rb +0 -196
- data/lib/parser/dsdl_parser.tab.rb +0 -976
- data/lib/spass/bin.rb +0 -164
- data/lib/spass/spass_ds_extensions.rb +0 -870
- data/lib/spass/spass_translator.rb +0 -388
- data/lib/spass/util.rb +0 -11
- data/lib/util/csv_hash_formatter.rb +0 -47
- data/lib/util/test_helper.rb +0 -33
- data/lib/util/util.rb +0 -114
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'backports'
|
2
|
+
require 'sexp_processor'
|
3
|
+
require 'ruby_parser'
|
4
|
+
require 'method_source'
|
5
|
+
require 'ruby2ruby'
|
6
|
+
require 'adsl/extract/sexp_utils'
|
7
|
+
require 'adsl/extract/meta'
|
8
|
+
|
9
|
+
module Kernel
|
10
|
+
def ins_call(object, method_name, *args, &block)
|
11
|
+
::ADSL::Extract::Instrumenter.get_instance.execute_instrumented object, method_name, *args, &block
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ADSL
|
16
|
+
module Extract
|
17
|
+
class Instrumenter
|
18
|
+
|
19
|
+
attr_reader :stack_depth, :method_locals_stack
|
20
|
+
attr_accessor :instrumentation_filters
|
21
|
+
|
22
|
+
@instance = nil
|
23
|
+
@method_locals_stack = []
|
24
|
+
|
25
|
+
def self.get_instance()
|
26
|
+
@instance
|
27
|
+
end
|
28
|
+
|
29
|
+
def ruby_parser
|
30
|
+
RUBY_VERSION >= '2' ? Ruby19Parser.new : RubyParser.for_current_ruby
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.instrumented()
|
34
|
+
# a dummy method injected into the AST
|
35
|
+
end
|
36
|
+
|
37
|
+
def method_locals
|
38
|
+
@method_locals_stack.last
|
39
|
+
end
|
40
|
+
|
41
|
+
def previous_locals
|
42
|
+
@method_locals_stack[-2]
|
43
|
+
end
|
44
|
+
|
45
|
+
def root_locals
|
46
|
+
@method_locals_stack.first
|
47
|
+
end
|
48
|
+
|
49
|
+
def exec_within
|
50
|
+
Instrumenter.instance_variable_set(:@instance, self) if @stack_depth == 0
|
51
|
+
@stack_depth += 1
|
52
|
+
@method_locals_stack << create_locals if respond_to? :create_locals
|
53
|
+
|
54
|
+
return yield(self)
|
55
|
+
ensure
|
56
|
+
@stack_depth -= 1
|
57
|
+
@method_locals_stack.pop
|
58
|
+
Instrumenter.instance_variable_set(:@instance, nil) if @stack_depth == 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def mark_sexp_instrumented(sexp)
|
62
|
+
raise 'Already instrumented' if sexp_instrumented? sexp
|
63
|
+
|
64
|
+
first_stmt = sexp[3]
|
65
|
+
|
66
|
+
if first_stmt[0] != :call or
|
67
|
+
first_stmt[1] != Instrumenter.to_sexp or
|
68
|
+
first_stmt[2] != :instrumented
|
69
|
+
new_stmt = s(:call, Instrumenter.to_sexp, :instrumented)
|
70
|
+
sexp.insert 3, new_stmt
|
71
|
+
end
|
72
|
+
sexp
|
73
|
+
end
|
74
|
+
|
75
|
+
def sexp_instrumented?(sexp)
|
76
|
+
first_stmt = sexp[3]
|
77
|
+
return (first_stmt[0] == :call and
|
78
|
+
first_stmt[1] == Instrumenter.to_sexp and
|
79
|
+
first_stmt[2] == :instrumented)
|
80
|
+
rescue MethodSource::SourceNotFoundError
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
def initialize(instrument_domain = Dir.pwd)
|
85
|
+
@instrument_domain = instrument_domain
|
86
|
+
@replacers = []
|
87
|
+
@stack_depth = 0
|
88
|
+
@method_locals_stack = []
|
89
|
+
|
90
|
+
# mark the instrumentation
|
91
|
+
replace :defn, :defs do |sexp|
|
92
|
+
mark_sexp_instrumented sexp
|
93
|
+
end
|
94
|
+
|
95
|
+
# make sure the instrumentation propagates through calls
|
96
|
+
replace :call do |sexp|
|
97
|
+
# expected format: s(:call, object, method_name, *args)
|
98
|
+
# replaced with Extract::Instrumenter.e(instrumenter_id, object, method_name, *args)
|
99
|
+
original_object = sexp.sexp_body[0] || s(:self)
|
100
|
+
original_method_name = sexp.sexp_body[1]
|
101
|
+
original_args = sexp.sexp_body[2..-1]
|
102
|
+
|
103
|
+
next sexp if [s(:self), nil].include? original_object and Kernel.respond_to? original_method_name
|
104
|
+
|
105
|
+
s(:call, nil, :ins_call, original_object, s(:lit, original_method_name), *original_args)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def should_instrument?(object, method_name)
|
110
|
+
return false if object.is_a?(Fixnum) or object.is_a?(Symbol)
|
111
|
+
|
112
|
+
method = object.singleton_class.instance_method method_name
|
113
|
+
|
114
|
+
return false if method.source_location.nil?
|
115
|
+
return false if method.owner == Kernel
|
116
|
+
return false if @instrument_domain && !(method.source_location.first =~ /^#{@instrument_domain}.*$/)
|
117
|
+
|
118
|
+
(instrumentation_filters || []).each do |filter|
|
119
|
+
return false unless filter.allow_instrumentation? object, method_name
|
120
|
+
end
|
121
|
+
|
122
|
+
source = method.source
|
123
|
+
sexp = ruby_parser.process source
|
124
|
+
!sexp_instrumented? sexp
|
125
|
+
rescue MethodSource::SourceNotFoundError
|
126
|
+
# sometimes this happens because the method_source gem bugs out with evals etc
|
127
|
+
return false
|
128
|
+
rescue NameError => e
|
129
|
+
# ghost method with no available source
|
130
|
+
return false
|
131
|
+
end
|
132
|
+
|
133
|
+
def replace(*types, &block)
|
134
|
+
options = types.last.is_a?(Hash) ? types.pop : {}
|
135
|
+
@replacers << [types, block, options]
|
136
|
+
end
|
137
|
+
|
138
|
+
def with_replace(*types, replacer)
|
139
|
+
replace *types, replacer
|
140
|
+
yield
|
141
|
+
ensure
|
142
|
+
@replacers.pop
|
143
|
+
end
|
144
|
+
|
145
|
+
def execute_instrumented(object, method_name, *args, &block)
|
146
|
+
self.exec_within do
|
147
|
+
instrument object, method_name
|
148
|
+
return object.send method_name, *args, &block
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def convert_root_defs_into_defn(sexp)
|
153
|
+
sexp.sexp_type == :defs ? s(:defn, *sexp[2..-1]) : sexp
|
154
|
+
end
|
155
|
+
|
156
|
+
def instrument_string(source)
|
157
|
+
sexp = ruby_parser.process source
|
158
|
+
unless sexp.nil?
|
159
|
+
instrumented_sexp = instrument_sexp sexp
|
160
|
+
new_code = Ruby2Ruby.new.process instrumented_sexp
|
161
|
+
else
|
162
|
+
source
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def instrument(object, method_name)
|
167
|
+
if should_instrument? object, method_name
|
168
|
+
begin
|
169
|
+
# this is complex because I want to avoid using .method on non-class objects
|
170
|
+
# because they might implement method themselves
|
171
|
+
method = object.singleton_class.instance_method method_name
|
172
|
+
|
173
|
+
source = method.source
|
174
|
+
|
175
|
+
# Ruby 2.0.0 support is in development as of writing this
|
176
|
+
sexp = ruby_parser.process source
|
177
|
+
|
178
|
+
unless sexp.nil?
|
179
|
+
sexp = convert_root_defs_into_defn sexp
|
180
|
+
|
181
|
+
instrumented_sexp = instrument_sexp sexp
|
182
|
+
|
183
|
+
new_code = Ruby2Ruby.new.process instrumented_sexp
|
184
|
+
|
185
|
+
object.replace_method method_name, new_code
|
186
|
+
|
187
|
+
new_code
|
188
|
+
else
|
189
|
+
source
|
190
|
+
end
|
191
|
+
rescue MethodSource::SourceNotFoundError
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def instrument_sexp(sexp)
|
197
|
+
return nil if sexp.nil?
|
198
|
+
@replacers.reverse_each do |types, block, options|
|
199
|
+
sexp = sexp.block_replace *types, options, &block
|
200
|
+
end
|
201
|
+
sexp
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'backports'
|
3
|
+
|
4
|
+
class Object
|
5
|
+
def replace_method(method_name, source = nil, &block)
|
6
|
+
raise "Object #{self} of class #{self.class} does not respond to #{method_name}" unless self.respond_to? method_name, true
|
7
|
+
|
8
|
+
im = self.singleton_class.instance_method(method_name)
|
9
|
+
|
10
|
+
aliases = []
|
11
|
+
self.singleton_class.instance_methods.each do |other_name|
|
12
|
+
next if other_name == method_name
|
13
|
+
other = self.singleton_class.instance_method other_name
|
14
|
+
aliases << [other_name, other] if other == im
|
15
|
+
end
|
16
|
+
|
17
|
+
owner = im.owner
|
18
|
+
|
19
|
+
unless source.nil?
|
20
|
+
owner.class_eval source
|
21
|
+
else
|
22
|
+
owner.send :define_method, method_name, &block
|
23
|
+
end
|
24
|
+
|
25
|
+
aliases.each do |other_name, other|
|
26
|
+
other.owner.class_exec do
|
27
|
+
alias_method other_name, method_name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'adsl/extract/rails/other_meta'
|
2
|
+
require 'adsl/parser/ast_nodes'
|
3
|
+
|
4
|
+
module ADSL
|
5
|
+
module Extract
|
6
|
+
module Rails
|
7
|
+
|
8
|
+
class ActionBlockBuilder
|
9
|
+
|
10
|
+
attr_accessor :root_paths, :stmt_frames, :branch_choices
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@root_paths = []
|
14
|
+
@stmt_frames = [[]]
|
15
|
+
@branch_choices = []
|
16
|
+
@return_values = []
|
17
|
+
@has_returned_or_raised = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def push_frame; @stmt_frames << []; end
|
21
|
+
def pop_frame; @stmt_frames.pop; end
|
22
|
+
|
23
|
+
def in_stmt_frame(*args)
|
24
|
+
push_frame
|
25
|
+
yield *args
|
26
|
+
ensure
|
27
|
+
return pop_frame
|
28
|
+
end
|
29
|
+
|
30
|
+
def included_already?(where, what)
|
31
|
+
return where.map{ |e| e.equal? what }.include?(true) ||
|
32
|
+
(
|
33
|
+
where.last.is_a?(ADSL::Parser::ASTObjsetStmt) &&
|
34
|
+
what.is_a?(ADSL::Parser::ASTObjsetStmt) &&
|
35
|
+
where.last.objset == what.objset
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def append_stmt(stmt, options = {})
|
40
|
+
return stmt if @has_returned_or_raised && !options[:ignore_has_returned]
|
41
|
+
return stmt if included_already? @stmt_frames.last, stmt
|
42
|
+
@stmt_frames.last << stmt
|
43
|
+
stmt
|
44
|
+
end
|
45
|
+
alias_method :<<, :append_stmt
|
46
|
+
|
47
|
+
def branch_choice(if_id)
|
48
|
+
@branch_choices.each do |iter_if_id, choice|
|
49
|
+
return choice if iter_if_id == if_id
|
50
|
+
end
|
51
|
+
@branch_choices << [if_id, true]
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_more_executions?
|
56
|
+
@branch_choices.each do |if_id, choice|
|
57
|
+
return true if choice == true
|
58
|
+
end
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
def increment_branch_choice
|
63
|
+
@branch_choices.pop while !@branch_choices.empty? && @branch_choices.last[1] == false
|
64
|
+
@branch_choices.last[1] = false unless @branch_choices.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
def reset
|
68
|
+
unless has_more_executions?
|
69
|
+
@root_paths = []
|
70
|
+
@branch_choices = []
|
71
|
+
@return_values = []
|
72
|
+
end
|
73
|
+
@has_returned_or_raised = false
|
74
|
+
@stmt_frames = [[]]
|
75
|
+
end
|
76
|
+
|
77
|
+
def explore_all_choices
|
78
|
+
while true
|
79
|
+
begin
|
80
|
+
reset
|
81
|
+
increment_branch_choice
|
82
|
+
|
83
|
+
return_value = yield
|
84
|
+
|
85
|
+
do_return return_value unless @has_returned_or_raised
|
86
|
+
rescue Exception => e
|
87
|
+
#puts "Exception: #{e}"
|
88
|
+
#puts e.backtrace.first 20
|
89
|
+
#do_raise unless @has_returned_or_raised
|
90
|
+
ensure
|
91
|
+
return common_return_value unless has_more_executions?
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def common_supertype_of_objsets(values)
|
97
|
+
return false if values.empty?
|
98
|
+
values.each do |value|
|
99
|
+
return false if value.is_a?(MetaUnknown) || !value.respond_to?(:adsl_ast)
|
100
|
+
end
|
101
|
+
adsl_asts = values.reject{ |v| v.nil? }.map(&:adsl_ast)
|
102
|
+
adsl_asts = adsl_asts.map{ |v| v.is_a?(ADSL::Parser::ASTObjsetStmt) ? v.objset : v }
|
103
|
+
adsl_asts.each do |adsl_ast|
|
104
|
+
return false unless adsl_ast.class.is_objset?
|
105
|
+
# side effects should trigger only if the selection is chosen;
|
106
|
+
# but the translation does not do this
|
107
|
+
return false if adsl_ast.objset_has_side_effects?
|
108
|
+
end
|
109
|
+
|
110
|
+
common_supertype = nil
|
111
|
+
values.each do |value|
|
112
|
+
next if value.nil?
|
113
|
+
if common_supertype.nil?
|
114
|
+
common_supertype = value.class
|
115
|
+
elsif value.class <= common_supertype
|
116
|
+
# all is fine
|
117
|
+
elsif common_supertype <= value.class
|
118
|
+
common_supertype = value.class
|
119
|
+
else
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
common_supertype
|
125
|
+
end
|
126
|
+
|
127
|
+
def common_supertype_of_objset_arrays(values)
|
128
|
+
return false if values.empty?
|
129
|
+
|
130
|
+
values.each do |value|
|
131
|
+
return false unless value.is_a? Array
|
132
|
+
end
|
133
|
+
|
134
|
+
return_value = []
|
135
|
+
highest_length = values.map(&:length).max
|
136
|
+
highest_length.times do |index|
|
137
|
+
ct = compatible_types(values.map{ |v| v[index] })
|
138
|
+
return false unless ct
|
139
|
+
return_value << ct
|
140
|
+
end
|
141
|
+
return_value
|
142
|
+
end
|
143
|
+
|
144
|
+
def common_return_value
|
145
|
+
uniq = @return_values.dup
|
146
|
+
# avoid include? because it uses :== and metaobjects override the == operator
|
147
|
+
if uniq.map(&:nil?).include? true
|
148
|
+
uniq.delete_if{ |e| e.nil? }
|
149
|
+
uniq << nil
|
150
|
+
end
|
151
|
+
|
152
|
+
if uniq.length == 1
|
153
|
+
uniq.first
|
154
|
+
elsif ct = common_supertype_of_objsets(uniq)
|
155
|
+
objsets = uniq.map(&:adsl_ast).map{ |r| r.is_a?(ADSL::Parser::ASTObjsetStmt) ? r.objset : r }
|
156
|
+
ct.new(:adsl_ast => ADSL::Parser::ASTOneOfObjset.new(:objsets => objsets))
|
157
|
+
elsif ct = common_supertype_of_objset_arrays(uniq)
|
158
|
+
highest_length = uniq.map(&:length).max
|
159
|
+
combined_objsets = []
|
160
|
+
highest_length.times do |index|
|
161
|
+
objsets = uniq.map{|u| u[index]}.map(&:adsl_ast).map{ |r| r.is_a?(ADSL::Parser::ASTObjsetStmt) ? r.objset : r }
|
162
|
+
combined_objsets << ct[index].new(:objset => ADSL::Parser::ASTOneOfObjset.new(:objsets => objsets))
|
163
|
+
end
|
164
|
+
combined_objsets
|
165
|
+
else
|
166
|
+
# append all return values to root paths
|
167
|
+
# cause they won't be returned and handled by the caller
|
168
|
+
# if an array is returned, assume the 'return 1, 2, 3' syntax
|
169
|
+
|
170
|
+
@return_values.length.times do |index|
|
171
|
+
Array.wrap(@return_values[index]).flatten.each do |ret_value|
|
172
|
+
stmt = ADSL::Extract::Rails::ActionInstrumenter.extract_stmt_from_expr ret_value
|
173
|
+
@root_paths[index] << stmt unless (
|
174
|
+
stmt.nil? ||
|
175
|
+
!stmt.class.is_statement? ||
|
176
|
+
included_already?(@root_paths[index], stmt)
|
177
|
+
)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
ADSL::Extract::Rails::MetaUnknown.new
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def all_stmts_so_far
|
186
|
+
stmts = []
|
187
|
+
@stmt_frames.each do |frame|
|
188
|
+
stmts += frame
|
189
|
+
end
|
190
|
+
stmts
|
191
|
+
end
|
192
|
+
|
193
|
+
def do_return(return_value = nil)
|
194
|
+
unless @has_returned_or_raised
|
195
|
+
@root_paths << all_stmts_so_far
|
196
|
+
@return_values << return_value
|
197
|
+
@has_returned_or_raised = true
|
198
|
+
end
|
199
|
+
return_value
|
200
|
+
end
|
201
|
+
|
202
|
+
def do_raise(*args)
|
203
|
+
unless @has_returned_or_raised
|
204
|
+
# appending nothing to root paths
|
205
|
+
@root_paths << [::ADSL::Parser::ASTDummyStmt.new(:type => :raise)]
|
206
|
+
@return_values << nil
|
207
|
+
@has_returned_or_raised = true
|
208
|
+
end
|
209
|
+
return *args
|
210
|
+
end
|
211
|
+
|
212
|
+
def adsl_ast
|
213
|
+
blocks = @root_paths.map do |root_path|
|
214
|
+
::ADSL::Parser::ASTBlock.new :statements => root_path
|
215
|
+
end
|
216
|
+
|
217
|
+
if blocks.empty?
|
218
|
+
::ADSL::Parser::ASTBlock.new :statements => []
|
219
|
+
elsif blocks.length == 1
|
220
|
+
blocks.first
|
221
|
+
else
|
222
|
+
::ADSL::Parser::ASTBlock.new :statements => [::ADSL::Parser::ASTEither.new(:blocks => blocks)]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def root_lvl_adsl_ast
|
227
|
+
::ADSL::Parser::ASTBlock.new :statements => @stmt_frames.first
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|