1
# Copyright (C) 2008-2009 Sun Microsystems, Inc. All rights reserved.
2
# Use is subject to license terms.
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; version 2 of the License.
8
# This program is distributed in the hope that it will be useful, but
9
# WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
# General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
18
package GenTest::Simplifier::Grammar;
27
use GenTest::Constants;
29
use GenTest::Grammar::Rule;
31
use constant SIMPLIFIER_ORACLE => 0;
32
use constant SIMPLIFIER_CACHE => 1;
33
use constant SIMPLIFIER_GRAMMAR_OBJ => 2;
34
use constant SIMPLIFIER_RULES_VISITED => 3;
35
use constant SIMPLIFIER_GRAMMAR_FLAGS => 4;
42
my $simplifier = $class->SUPER::new({
43
'oracle' => SIMPLIFIER_ORACLE,
44
'grammar_flags' => SIMPLIFIER_GRAMMAR_FLAGS
51
my ($simplifier, $initial_grammar_string) = @_;
53
if ($simplifier->oracle($initial_grammar_string) == ORACLE_ISSUE_NO_LONGER_REPEATABLE) {
54
warn("Initial grammar failed oracle check.");
55
warn("Are duration and/or trials too small or is a different value for seed required?");
59
my $grammar_string = $initial_grammar_string;
62
# We perform the descend() several times, in order to compensate for
63
# our imperfect tree walking algorithm combined with the probability of
64
# loops in the grammar files.
67
foreach my $trial (0..1) {
68
$simplifier->[SIMPLIFIER_GRAMMAR_OBJ] = GenTest::Grammar->new(
69
grammar_string => $grammar_string,
70
grammar_flags => $simplifier->[SIMPLIFIER_GRAMMAR_FLAGS]
73
return undef if not defined $simplifier->[SIMPLIFIER_GRAMMAR_OBJ];
75
$simplifier->[SIMPLIFIER_RULES_VISITED] = {};
77
$simplifier->descend('query');
79
foreach my $rule (keys %{$simplifier->[SIMPLIFIER_GRAMMAR_OBJ]->rules()}) {
80
if (not exists $simplifier->[SIMPLIFIER_RULES_VISITED]->{$rule}) {
81
# say("Rule $rule is not referenced any more. Removing from grammar.");
82
$simplifier->[SIMPLIFIER_GRAMMAR_OBJ]->deleteRule($rule);
86
$grammar_string = $simplifier->[SIMPLIFIER_GRAMMAR_OBJ]->toString();
89
if ($simplifier->oracle($grammar_string) == ORACLE_ISSUE_NO_LONGER_REPEATABLE) {
90
warn("Final grammar failed oracle check.");
93
return $grammar_string;
98
my ($simplifier, $rule) = @_;
100
my $grammar_obj = $simplifier->[SIMPLIFIER_GRAMMAR_OBJ];
102
my $rule_obj = $grammar_obj->rule($rule);
103
return $rule if not defined $rule_obj;
105
return $rule_obj if exists $simplifier->[SIMPLIFIER_RULES_VISITED]->{$rule};
106
$simplifier->[SIMPLIFIER_RULES_VISITED]->{$rule}++;
108
my $orig_components = $rule_obj->components();
110
for (my $component_id = $#$orig_components; $component_id >= 0; $component_id--) {
111
my $orig_component = $orig_components->[$component_id];
113
# Remove one component and call the oracle to check if the issue is still repeatable
115
say("Attempting to remove component ".join(' ', @$orig_component)." ...");
117
splice (@$orig_components, $component_id, 1);
119
if ($simplifier->oracle($grammar_obj->toString()) != ORACLE_ISSUE_NO_LONGER_REPEATABLE) {
120
say("Outcome still repeatable after removing ".join(' ', @$orig_component).". Deleting component.");
123
say("Outcome no longer repeatable after removing ".join(' ', @$orig_component).". Keeping component.");
125
# Undo the change and dig deeper, into the parts of the rule component
127
splice (@$orig_components, $component_id, 0, $orig_component);
129
for (my $part_id = $#{$orig_components->[$component_id]}; $part_id >= 0; $part_id--) {
131
my $child = $simplifier->descend($orig_components->[$component_id]->[$part_id]);
133
# If the outcome of the descend() is sufficiently simple, in-line it.
135
if (ref($child) eq 'GenTest::Grammar::Rule') {
136
my $child_name = $child->name();
137
if ($#{$child->components()} == -1) {
138
# say("Child $child_name is empty. Removing altogether.");
139
splice(@{$orig_components->[$component_id]}, $part_id, 1);
140
} elsif ($#{$child->components()} == 0) {
141
# say("Child $child_name has a single component. In-lining.");
142
splice(@{$orig_components->[$component_id]}, $part_id, 1, @{$child->components()->[0]});
145
# say("Got a string literal. In-lining.");
146
splice(@{$orig_components->[$component_id]}, $part_id, 1, $child);
156
my ($simplifier, $grammar) = @_;
158
my $cache = $simplifier->[SIMPLIFIER_CACHE];
159
my $oracle = $simplifier->[SIMPLIFIER_ORACLE];
161
$cache->{$grammar} = $oracle->($grammar) if not exists $cache->{$grammar};
162
return $cache->{$grammar};