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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebe37d86fc7f4beca92d9a2774ea9261f65bd3fa
|
4
|
+
data.tar.gz: 81b1927dd5809ded4855b31283a53222160160e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2d4e8b5fb36b758459a10309ca14fef2007894f73b26301c6eb6c50ed72f7d61a829a7a5fe1606f86e46b0a40488df6950d2a1582bbb96ebd9f5036a9dc839f
|
7
|
+
data.tar.gz: ecd21de07c4f79acd1b7dd4f16240dd02adfb8ba96b0f570e937b48cff376e7925450069898ae9389e88589f15814e8fab2109656538d347b2f83765d10374b6
|
data/Gemfile
CHANGED
@@ -1,20 +1,2 @@
|
|
1
|
-
source
|
2
|
-
|
3
|
-
# base
|
4
|
-
# indentation added to prevent these dependencies when building the gem
|
5
|
-
gem "rake"
|
6
|
-
gem "test-unit"
|
7
|
-
gem "i18n"
|
8
|
-
|
9
|
-
# lexer/parser
|
10
|
-
gem "rexical"
|
11
|
-
gem "racc"
|
12
|
-
|
13
|
-
# used all over the place
|
14
|
-
gem "active_support"
|
15
|
-
|
16
|
-
# terminal color output
|
17
|
-
gem "colorize"
|
18
|
-
|
19
|
-
# extraction tools
|
20
|
-
gem "ruby_parser"
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
gemspec
|
data/README.md
CHANGED
@@ -1,17 +1,15 @@
|
|
1
|
-
ADSL - Abstract Data Store
|
1
|
+
ADSL - Abstract Data Store Library
|
2
2
|
=========================================================
|
3
3
|
|
4
|
-
|
4
|
+
ADSL is a gem for formal verification of Ruby on Rails models. Simply include it in your Gemfile, write a few
|
5
|
+
invariants (rules) about ActiveRecord data that you wish to verify (for example, that at any given moment, every
|
6
|
+
Address has a User) and run `rake verify`!
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
using a RESTful interface, and a set of invariants to be verified.
|
8
|
+
Besides the verification algorithm, this tool includes a DSL for specifying invariants. The syntax should feel
|
9
|
+
natural to any Rails user. Look at /examples.
|
9
10
|
|
10
|
-
This tool
|
11
|
-
|
12
|
-
CSV format.
|
13
|
-
|
14
|
-
This tool is distributed as a Ruby gem and is uploaded to [RubyGems.org] [2]. Install it and give it a try!
|
11
|
+
This tool is distributed as a Ruby gem and is uploaded to [RubyGems.org] [2]. It requires Spass[1] to run. Install
|
12
|
+
it and give it a try!
|
15
13
|
|
16
14
|
|
17
15
|
Installation
|
@@ -20,24 +18,19 @@ Installation
|
|
20
18
|
This gem is tested on 32 and 64 bit Linux. OS-X compatibility not tested, give it a try
|
21
19
|
and tell us if it works! Windows is not supported at this moment.
|
22
20
|
|
23
|
-
- Ruby 1.
|
21
|
+
- Ruby 1.9.3 or later required, along with Rails 3.2. We suggest using the [Ruby Version Manager](https://rvm.io/rvm/install/) to manage this installation.
|
24
22
|
- [Download and install Spass](http://www.spass-prover.org/download/index.html) and make sure its executable (`bin/SPASS`) on your $PATH
|
25
23
|
- Install the ADSL gem by running `gem install adsl`.
|
26
|
-
|
27
24
|
If you receive an error while generating documentation for 'activesupport' run `gem install rdoc adsl` instead.
|
28
|
-
|
29
|
-
|
25
|
+
|
30
26
|
Usage
|
31
27
|
-----
|
32
28
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
adsl-verify --help
|
38
|
-
|
39
|
-
You can download sample ADSL specifications
|
29
|
+
rake verify <options>
|
30
|
+
|
31
|
+
Or, to just observe the extracted model,
|
40
32
|
|
33
|
+
rake adsl_translate
|
41
34
|
|
42
35
|
Development
|
43
36
|
-----------
|
data/bin/adsl-verify
CHANGED
@@ -9,7 +9,7 @@ options.language = 'spass'
|
|
9
9
|
options.halt_on_error = false
|
10
10
|
options.check_satisfiability = true
|
11
11
|
options.timeout = 30
|
12
|
-
options.
|
12
|
+
options.output = 'terminal'
|
13
13
|
options.actions = nil
|
14
14
|
options.invariants = nil
|
15
15
|
|
@@ -36,10 +36,10 @@ OptionParser.new do |opts|
|
|
36
36
|
options.check_satisfiability = check
|
37
37
|
end
|
38
38
|
|
39
|
-
opts.on("-
|
40
|
-
"
|
41
|
-
" Default: #{options.
|
42
|
-
options.
|
39
|
+
opts.on("-o", "--output-format FORMAT",
|
40
|
+
"Sets the output format. Options: terminal, csv, or silent",
|
41
|
+
" Default: #{options.output}") do |format|
|
42
|
+
options.output = format
|
43
43
|
end
|
44
44
|
|
45
45
|
opts.on("-t", "--timeout TIMEOUT", Integer,
|
@@ -87,14 +87,14 @@ $LOAD_PATH.unshift GEM_LIB_PATH unless $LOAD_PATH.include? GEM_LIB_PATH
|
|
87
87
|
|
88
88
|
case options.language
|
89
89
|
when 'spass'
|
90
|
-
require 'spass/bin'
|
91
|
-
include Spass::Bin
|
90
|
+
require 'adsl/spass/bin'
|
91
|
+
include ADSL::Spass::Bin
|
92
92
|
begin
|
93
93
|
verify(input,
|
94
94
|
:halt_on_error => options.halt_on_error,
|
95
95
|
:check_satisfiability => options.check_satisfiability,
|
96
96
|
:timeout => options.timeout,
|
97
|
-
:
|
97
|
+
:output => options.output,
|
98
98
|
:actions => options.actions,
|
99
99
|
:invariants => options.invariants)
|
100
100
|
rescue Exception => e
|
data/lib/adsl.rb
ADDED
data/lib/adsl/adsl.rb
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
require 'adsl/util/general'
|
2
|
+
|
3
|
+
module ADSL
|
4
|
+
module DS
|
5
|
+
class DSNode
|
6
|
+
def list_entity_classes_written_to
|
7
|
+
recursively_gather :entity_class_writes
|
8
|
+
end
|
9
|
+
|
10
|
+
def list_entity_classes_read
|
11
|
+
recursively_gather :entity_class_reads
|
12
|
+
end
|
13
|
+
|
14
|
+
def replace(what, with)
|
15
|
+
to_inspect = [self]
|
16
|
+
inspected = Set.new
|
17
|
+
replaced = false
|
18
|
+
while not to_inspect.empty?
|
19
|
+
elem = to_inspect.pop
|
20
|
+
if elem.kind_of? Array
|
21
|
+
elem.length.times do |i|
|
22
|
+
if elem[i] == what
|
23
|
+
elem[i] = with
|
24
|
+
replaced = true
|
25
|
+
else
|
26
|
+
to_inspect << elem[i] unless inspected.include? elem[i]
|
27
|
+
end
|
28
|
+
inspected << elem[i]
|
29
|
+
end
|
30
|
+
elsif elem.class.methods.include? 'container_for_fields' or elem.class.methods.include? :container_for_fields
|
31
|
+
elem.class.container_for_fields.each do |field_name|
|
32
|
+
field_val = elem.send field_name
|
33
|
+
if field_val == what
|
34
|
+
elem.send "#{field_name}=", with
|
35
|
+
replaced = true
|
36
|
+
elsif field_val.kind_of?(Array) or field_val.class.methods.include?('container_for_fields')
|
37
|
+
to_inspect << field_val unless inspected.include? field_val
|
38
|
+
end
|
39
|
+
inspected << field_val
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
replaced
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class DSSpec < DSNode
|
48
|
+
container_for :classes, :actions, :invariants
|
49
|
+
end
|
50
|
+
|
51
|
+
class DSClass < DSNode
|
52
|
+
container_for :name, :parent, :relations, :inverse_relations do
|
53
|
+
@relations = [] if @relations.nil?
|
54
|
+
@inverse_relations = [] if @inverse_relations.nil?
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
@name
|
59
|
+
end
|
60
|
+
|
61
|
+
def superclass_of?(other_class)
|
62
|
+
until other_class.nil?
|
63
|
+
return true if other_class == self
|
64
|
+
other_class = other_class.parent
|
65
|
+
end
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.common_supertype(classes)
|
70
|
+
types = classes.uniq
|
71
|
+
types.delete nil
|
72
|
+
while types.length > 1
|
73
|
+
type1 = types.pop
|
74
|
+
type2 = types.pop
|
75
|
+
if type1.superclass_of? type2
|
76
|
+
types << type2
|
77
|
+
elsif type2.superclass_of? type1
|
78
|
+
types << type1
|
79
|
+
else
|
80
|
+
raise ADSLError, "Object sets are not of compatible types: #{classes.map { |c| c.name }.join(", ")}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
types.first
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class DSRelation < DSNode
|
88
|
+
container_for :cardinality, :from_class, :to_class, :name, :inverse_of
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
"#{from_class.name}.#{name}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class DSAction < DSNode
|
96
|
+
container_for :name, :args, :cardinalities, :block
|
97
|
+
|
98
|
+
def statements
|
99
|
+
@block.statements
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class DSBlock < DSNode
|
104
|
+
container_for :statements
|
105
|
+
end
|
106
|
+
|
107
|
+
class DSAssignment < DSNode
|
108
|
+
container_for :var, :objset
|
109
|
+
end
|
110
|
+
|
111
|
+
class DSCreateObj < DSNode
|
112
|
+
container_for :klass
|
113
|
+
|
114
|
+
def entity_class_writes
|
115
|
+
Set[@klass]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class DSCreateObjset < DSNode
|
120
|
+
container_for :createobj
|
121
|
+
|
122
|
+
def type
|
123
|
+
@createobj.klass
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class DSCreateTup < DSNode
|
128
|
+
container_for :objset1, :relation, :objset2
|
129
|
+
end
|
130
|
+
|
131
|
+
class DSDeleteObj < DSNode
|
132
|
+
container_for :objset
|
133
|
+
|
134
|
+
def entity_class_writes
|
135
|
+
Set[@objset.type]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class DSDeleteTup < DSNode
|
140
|
+
container_for :objset1, :relation, :objset2
|
141
|
+
end
|
142
|
+
|
143
|
+
class DSEither < DSNode
|
144
|
+
container_for :blocks, :lambdas
|
145
|
+
end
|
146
|
+
|
147
|
+
class DSEitherLambdaObjset < DSNode
|
148
|
+
container_for :either, :vars
|
149
|
+
end
|
150
|
+
|
151
|
+
class DSForEachCommon < DSNode
|
152
|
+
container_for :objset, :block
|
153
|
+
end
|
154
|
+
|
155
|
+
class DSForEach < DSForEachCommon
|
156
|
+
end
|
157
|
+
|
158
|
+
class DSFlatForEach < DSForEachCommon
|
159
|
+
end
|
160
|
+
|
161
|
+
class DSForEachIteratorObjset < DSNode
|
162
|
+
container_for :for_each
|
163
|
+
|
164
|
+
def typecheck_and_resolve(context)
|
165
|
+
self
|
166
|
+
end
|
167
|
+
|
168
|
+
def type
|
169
|
+
@for_each.objset.type
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class DSForEachPreLambdaObjset < DSNode
|
174
|
+
container_for :for_each, :before_var, :inside_var
|
175
|
+
end
|
176
|
+
|
177
|
+
class DSForEachPostLambdaObjset < DSNode
|
178
|
+
container_for :for_each, :before_var, :inside_var
|
179
|
+
end
|
180
|
+
|
181
|
+
class DSVariable < DSNode
|
182
|
+
container_for :name, :type
|
183
|
+
end
|
184
|
+
|
185
|
+
class DSAllOf < DSNode
|
186
|
+
container_for :klass
|
187
|
+
|
188
|
+
def type
|
189
|
+
@klass
|
190
|
+
end
|
191
|
+
|
192
|
+
def entity_class_reads
|
193
|
+
@klass
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class DSSubset < DSNode
|
198
|
+
container_for :objset
|
199
|
+
|
200
|
+
def type
|
201
|
+
@objset.type
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class DSUnion < DSNode
|
206
|
+
container_for :objsets
|
207
|
+
|
208
|
+
def type
|
209
|
+
DSClass.common_supertype objsets.reject{ |o| o.type.nil? }.map{ |o| o.type }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
class DSOneOfObjset < DSNode
|
214
|
+
container_for :objsets
|
215
|
+
|
216
|
+
def type
|
217
|
+
DSClass.common_supertype objsets.reject{ |o| o.type.nil? }.map{ |o| o.type }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class DSOneOf < DSNode
|
222
|
+
container_for :objset
|
223
|
+
def type
|
224
|
+
@objset.type
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
class DSDereference < DSNode
|
229
|
+
container_for :objset, :relation
|
230
|
+
|
231
|
+
def type
|
232
|
+
@relation.to_class
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
class DSEmptyObjset < DSNode
|
237
|
+
container_for
|
238
|
+
|
239
|
+
def type
|
240
|
+
nil
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class DSInvariant < DSNode
|
245
|
+
container_for :name, :formula
|
246
|
+
end
|
247
|
+
|
248
|
+
class DSBoolean < DSNode
|
249
|
+
container_for :bool_value
|
250
|
+
|
251
|
+
TRUE = DSBoolean.new :bool_value => true
|
252
|
+
FALSE = DSBoolean.new :bool_value => false
|
253
|
+
|
254
|
+
def type
|
255
|
+
:formula
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class DSForAll < DSNode
|
260
|
+
container_for :vars, :objsets, :subformula
|
261
|
+
|
262
|
+
def type
|
263
|
+
:formula
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class DSExists < DSNode
|
268
|
+
container_for :vars, :objsets, :subformula
|
269
|
+
|
270
|
+
def type
|
271
|
+
:formula
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
class DSIn < DSNode
|
276
|
+
container_for :objset1, :objset2
|
277
|
+
|
278
|
+
def type
|
279
|
+
:formula
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class DSEmpty < DSNode
|
284
|
+
container_for :objset
|
285
|
+
|
286
|
+
def type
|
287
|
+
:formula
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
class DSNot < DSNode
|
292
|
+
container_for :subformula
|
293
|
+
|
294
|
+
def type
|
295
|
+
:formula
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class DSAnd < DSNode
|
300
|
+
container_for :subformulae
|
301
|
+
|
302
|
+
def type
|
303
|
+
:formula
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
class DSOr < DSNode
|
308
|
+
container_for :subformulae
|
309
|
+
|
310
|
+
def type
|
311
|
+
:formula
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
class DSImplies < DSNode
|
316
|
+
container_for :subformula1, :subformula2
|
317
|
+
|
318
|
+
def type
|
319
|
+
:formula
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
class DSEquiv < DSNode
|
324
|
+
container_for :subformulae
|
325
|
+
|
326
|
+
def type
|
327
|
+
:formula
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
class DSEqual < DSNode
|
332
|
+
container_for :objsets
|
333
|
+
|
334
|
+
def type
|
335
|
+
:formula
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|