package com.sun.tools.javac.comp;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.BindingSymbol;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.Tag;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Log;
import static com.sun.tools.javac.code.Flags.CLASH;
public class MatchBindingsComputer extends TreeScanner {
public static final MatchBindings EMPTY = new MatchBindings(List.nil(), List.nil());
protected static final Context.Key<MatchBindingsComputer> matchBindingsComputerKey = new Context.Key<>();
private final Log log;
public static MatchBindingsComputer instance(Context context) {
MatchBindingsComputer instance = context.get(matchBindingsComputerKey);
if (instance == null)
instance = new MatchBindingsComputer(context);
return instance;
}
protected MatchBindingsComputer(Context context) {
this.log = Log.instance(context);
}
public MatchBindings conditional(JCTree tree, MatchBindings condBindings, MatchBindings trueBindings, MatchBindings falseBindings) {
if (condBindings == EMPTY &&
trueBindings == EMPTY &&
falseBindings == EMPTY) {
return EMPTY;
}
DiagnosticPosition pos = tree.pos();
List<BindingSymbol> xTzT = intersection(pos, condBindings.bindingsWhenTrue, falseBindings.bindingsWhenTrue);
List<BindingSymbol> xFyT = intersection(pos, condBindings.bindingsWhenFalse, trueBindings.bindingsWhenTrue);
List<BindingSymbol> yTzT = intersection(pos, trueBindings.bindingsWhenTrue, falseBindings.bindingsWhenTrue);
List<BindingSymbol> xTzF = intersection(pos, condBindings.bindingsWhenTrue, falseBindings.bindingsWhenFalse);
List<BindingSymbol> xFyF = intersection(pos, condBindings.bindingsWhenFalse, trueBindings.bindingsWhenFalse);
List<BindingSymbol> yFzF = intersection(pos, trueBindings.bindingsWhenFalse, falseBindings.bindingsWhenFalse);
List<BindingSymbol> bindingsWhenTrue = union(pos, yTzT, xTzT, xFyT);
List<BindingSymbol> bindingsWhenFalse = union(pos, yFzF, xTzF, xFyF);
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
}
public MatchBindings unary(JCTree tree, MatchBindings bindings) {
if (bindings == EMPTY || !tree.hasTag(Tag.NOT)) return bindings;
return new MatchBindings(bindings.bindingsWhenFalse, bindings.bindingsWhenTrue);
}
public MatchBindings binary(JCTree tree, MatchBindings lhsBindings, MatchBindings rhsBindings) {
switch (tree.getTag()) {
case AND: {
List<BindingSymbol> bindingsWhenTrue =
union(tree.pos(), lhsBindings.bindingsWhenTrue, rhsBindings.bindingsWhenTrue);
List<BindingSymbol> bindingsWhenFalse =
intersection(tree.pos(), lhsBindings.bindingsWhenFalse, rhsBindings.bindingsWhenFalse);
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
}
case OR: {
List<BindingSymbol> bindingsWhenTrue =
intersection(tree.pos(), lhsBindings.bindingsWhenTrue, rhsBindings.bindingsWhenTrue);
List<Symbol.BindingSymbol> bindingsWhenFalse =
union(tree.pos(), lhsBindings.bindingsWhenFalse, rhsBindings.bindingsWhenFalse);
return new MatchBindings(bindingsWhenTrue, bindingsWhenFalse);
}
}
return EMPTY;
}
public MatchBindings finishBindings(JCTree tree, MatchBindings matchBindings) {
switch (tree.getTag()) {
case NOT: case AND: case OR: case BINDINGPATTERN:
case PARENS: case TYPETEST:
case CONDEXPR:
return matchBindings;
default:
return MatchBindingsComputer.EMPTY;
}
}
public static class MatchBindings {
public final List<BindingSymbol> bindingsWhenTrue;
public final List<BindingSymbol> bindingsWhenFalse;
public MatchBindings(List<BindingSymbol> bindingsWhenTrue, List<BindingSymbol> bindingsWhenFalse) {
this.bindingsWhenTrue = bindingsWhenTrue;
this.bindingsWhenFalse = bindingsWhenFalse;
}
}
private List<BindingSymbol> intersection(DiagnosticPosition pos, List<BindingSymbol> lhsBindings, List<BindingSymbol> rhsBindings) {
List<BindingSymbol> list = List.nil();
for (BindingSymbol v1 : lhsBindings) {
for (BindingSymbol v2 : rhsBindings) {
if (v1.name == v2.name &&
(v1.flags() & CLASH) == 0 &&
(v2.flags() & CLASH) == 0) {
log.error(pos, Errors.MatchBindingExists);
v2.flags_field |= CLASH;
list = list.append(v2);
}
}
}
return list;
}
@SafeVarargs
private final List<BindingSymbol> union(DiagnosticPosition pos, List<BindingSymbol> lhsBindings, List<BindingSymbol> ... rhsBindings_s) {
List<BindingSymbol> list = lhsBindings;
for (List<BindingSymbol> rhsBindings : rhsBindings_s) {
for (BindingSymbol v : rhsBindings) {
for (BindingSymbol ov : list) {
if (ov.name == v.name &&
(ov.flags() & CLASH) == 0 &&
(v.flags() & CLASH) == 0) {
log.error(pos, Errors.MatchBindingExists);
v.flags_field |= CLASH;
}
}
list = list.append(v);
}
}
return list;
}
}