// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package wyil.checks;
import java.util.*;
import wybs.lang.Builder;
import wycc.lang.Transform;
import wycc.util.Pair;
import wyfs.lang.Path;
import wyil.util.*;
import wyil.util.dfa.*;
import wyil.lang.*;
import static wycc.lang.SyntaxError.*;
import static wyil.lang.Code.Block.*;
import static wyil.util.ErrorMessages.*;
/**
* <p>
* The purpose of this class is to check that all variables are defined before
* being used. For example:
* </p>
*
* <pre>
* int f() {
* int z;
* return z + 1;
* }
* </pre>
*
* <p>
* In the above example, variable z is used in the return statement before it
* has been defined any value. This is considered a syntax error in whiley.
* </p>
* @author David J. Pearce
*
*/
public class DefiniteAssignmentCheck extends
ForwardFlowAnalysis<HashSet<Integer>> implements Transform<WyilFile> {
public DefiniteAssignmentCheck(Builder builder) {
}
@Override
public HashSet<Integer> initialStore() {
HashSet<Integer> defined = new HashSet<Integer>();
int diff = 0;
for(int i=0;i!=method.type().params().size();++i) {
defined.add(i+diff);
}
return defined;
}
@Override
public HashSet<Integer> propagate(int idx, Entry entry, HashSet<Integer> in) {
Code code = entry.code;
checkUses(code,entry,in);
int def = defs(code,entry);
if(def >= 0) {
in = new HashSet<Integer>(in);
in.add(def);
}
return in;
}
@Override
public Pair<HashSet<Integer>, HashSet<Integer>> propagate(int index,
Codes.If igoto, Entry entry, HashSet<Integer> in) {
if (!in.contains(igoto.leftOperand) || !in.contains(igoto.rightOperand)) {
syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename,
entry);
}
return new Pair(in, in);
}
@Override
public Pair<HashSet<Integer>, HashSet<Integer>> propagate(int index,
Codes.IfIs iftype, Entry entry, HashSet<Integer> in) {
if (!in.contains(iftype.operand)) {
syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename,
entry);
}
return new Pair(in,in);
}
@Override
public List<HashSet<Integer>> propagate(int index, Codes.Switch sw,
Entry entry, HashSet<Integer> in) {
if (!in.contains(sw.operand)) {
syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED), filename,
entry);
}
ArrayList<HashSet<Integer>> stores = new ArrayList();
for (int i = 0; i != sw.branches.size(); ++i) {
stores.add(in);
}
return stores;
}
@Override
public HashSet<Integer> propagate(Type handler, Codes.TryCatch tc, HashSet<Integer> in) {
in = new HashSet<Integer>(in);
in.add(tc.operand);
return in;
}
@Override
public HashSet<Integer> propagate(int start, int end, Codes.Loop loop,
Entry entry, HashSet<Integer> in, List<Codes.TryCatch> handlers) {
if (loop instanceof Codes.ForAll) {
Codes.ForAll fall = (Codes.ForAll) loop;
if (!in.contains(fall.sourceOperand)) {
syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED),
filename, entry);
}
in = new HashSet<Integer>(in);
in.add(fall.indexOperand);
}
HashSet<Integer> r = propagate(start + 1, end, in, handlers);
return join(in, r);
}
protected HashSet<Integer> join(HashSet<Integer> s1, HashSet<Integer> s2) {
HashSet<Integer> r = new HashSet<Integer>();
// set intersection
for (Integer s : s1) {
if (s2.contains(s)) {
r.add(s);
}
}
return r;
}
public void checkUses(Code code, Entry entry, HashSet<Integer> in) {
if(code instanceof Code.AbstractUnaryOp) {
Code.AbstractUnaryOp a = (Code.AbstractUnaryOp) code;
if(a.operand == Codes.NULL_REG || in.contains(a.operand)) {
return;
}
} else if(code instanceof Code.AbstractBinaryOp) {
Code.AbstractBinaryOp a = (Code.AbstractBinaryOp) code;
if (in.contains(a.leftOperand) && in.contains(a.rightOperand)) {
return;
}
} else if(code instanceof Code.AbstractNaryAssignable) {
Code.AbstractNaryAssignable a = (Code.AbstractNaryAssignable) code;
for(int operand : a.operands()) {
if(operand != Codes.NULL_REG && !in.contains(operand)) {
syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED),
filename, entry);
}
}
if(code instanceof Codes.Update && !in.contains(a.target())) {
// In this case, we are assigning to an index or field.
// Therefore, the target register must already be defined.
syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED),
filename, entry);
}
return;
} else {
// includes abstract-assignables and branching bytecodes
return;
}
syntaxError(errorMessage(VARIABLE_POSSIBLY_UNITIALISED),
filename, entry);
}
public int defs(Code code, Entry entry) {
if (code instanceof Code.AbstractAssignable) {
Code.AbstractAssignable aa = (Code.AbstractAssignable) code;
return aa.target();
}
return Codes.NULL_REG;
}
}