|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- /*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later,
- * or the Apache License Version 2.0.
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- */
- package javassist.bytecode.analysis;
-
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
-
- import javassist.CtClass;
-
- /**
- * MultiType represents an unresolved type. Whenever two {@code Type}
- * instances are merged, if they share more than one super type (either an
- * interface or a superclass), then a {@code MultiType} is used to
- * represent the possible super types. The goal of a {@code MultiType}
- * is to reduce the set of possible types down to a single resolved type. This
- * is done by eliminating non-assignable types from the typeset when the
- * {@code MultiType} is passed as an argument to
- * {@link Type#isAssignableFrom(Type)}, as well as removing non-intersecting
- * types during a merge.
- *
- * Note: Currently the {@code MultiType} instance is reused as much
- * as possible so that updates are visible from all frames. In addition, all
- * {@code MultiType} merge paths are also updated. This is somewhat
- * hackish, but it appears to handle most scenarios.
- *
- * @author Jason T. Greene
- */
-
- /* TODO - A better, but more involved, approach would be to track the instruction
- * offset that resulted in the creation of this type, and
- * whenever the typeset changes, to force a merge on that position. This
- * would require creating a new MultiType instance every time the typeset
- * changes, and somehow communicating assignment changes to the Analyzer
- */
- public class MultiType extends Type {
- private Map interfaces;
- private Type resolved;
- private Type potentialClass;
- private MultiType mergeSource;
- private boolean changed = false;
-
- public MultiType(Map interfaces) {
- this(interfaces, null);
- }
-
- public MultiType(Map interfaces, Type potentialClass) {
- super(null);
- this.interfaces = interfaces;
- this.potentialClass = potentialClass;
- }
-
- /**
- * Gets the class that corresponds with this type. If this information
- * is not yet known, java.lang.Object will be returned.
- */
- public CtClass getCtClass() {
- if (resolved != null)
- return resolved.getCtClass();
-
- return Type.OBJECT.getCtClass();
- }
-
- /**
- * Always returns null since this type is never used for an array.
- */
- public Type getComponent() {
- return null;
- }
-
- /**
- * Always returns 1, since this type is a reference.
- */
- public int getSize() {
- return 1;
- }
-
- /**
- * Always reutnrs false since this type is never used for an array
- */
- public boolean isArray() {
- return false;
- }
-
- /**
- * Returns true if the internal state has changed.
- */
- boolean popChanged() {
- boolean changed = this.changed;
- this.changed = false;
- return changed;
- }
-
- public boolean isAssignableFrom(Type type) {
- throw new UnsupportedOperationException("Not implemented");
- }
-
- public boolean isAssignableTo(Type type) {
- if (resolved != null)
- return type.isAssignableFrom(resolved);
-
- if (Type.OBJECT.equals(type))
- return true;
-
- if (potentialClass != null && !type.isAssignableFrom(potentialClass))
- potentialClass = null;
-
- Map map = mergeMultiAndSingle(this, type);
-
- if (map.size() == 1 && potentialClass == null) {
- // Update previous merge paths to the same resolved type
- resolved = Type.get((CtClass)map.values().iterator().next());
- propogateResolved();
-
- return true;
- }
-
- // Keep all previous merge paths up to date
- if (map.size() >= 1) {
- interfaces = map;
- propogateState();
-
- return true;
- }
-
- if (potentialClass != null) {
- resolved = potentialClass;
- propogateResolved();
-
- return true;
- }
-
- return false;
- }
-
- private void propogateState() {
- MultiType source = mergeSource;
- while (source != null) {
- source.interfaces = interfaces;
- source.potentialClass = potentialClass;
- source = source.mergeSource;
- }
- }
-
- private void propogateResolved() {
- MultiType source = mergeSource;
- while (source != null) {
- source.resolved = resolved;
- source = source.mergeSource;
- }
- }
-
- /**
- * Always returns true, since this type is always a reference.
- *
- * @return true
- */
- public boolean isReference() {
- return true;
- }
-
- private Map getAllMultiInterfaces(MultiType type) {
- Map map = new HashMap();
-
- Iterator iter = type.interfaces.values().iterator();
- while (iter.hasNext()) {
- CtClass intf = (CtClass)iter.next();
- map.put(intf.getName(), intf);
- getAllInterfaces(intf, map);
- }
-
- return map;
- }
-
-
- private Map mergeMultiInterfaces(MultiType type1, MultiType type2) {
- Map map1 = getAllMultiInterfaces(type1);
- Map map2 = getAllMultiInterfaces(type2);
-
- return findCommonInterfaces(map1, map2);
- }
-
- private Map mergeMultiAndSingle(MultiType multi, Type single) {
- Map map1 = getAllMultiInterfaces(multi);
- Map map2 = getAllInterfaces(single.getCtClass(), null);
-
- return findCommonInterfaces(map1, map2);
- }
-
- private boolean inMergeSource(MultiType source) {
- while (source != null) {
- if (source == this)
- return true;
-
- source = source.mergeSource;
- }
-
- return false;
- }
-
- public Type merge(Type type) {
- if (this == type)
- return this;
-
- if (type == UNINIT)
- return this;
-
- if (type == BOGUS)
- return BOGUS;
-
- if (type == null)
- return this;
-
- if (resolved != null)
- return resolved.merge(type);
-
- if (potentialClass != null) {
- Type mergePotential = potentialClass.merge(type);
- if (! mergePotential.equals(potentialClass) || mergePotential.popChanged()) {
- potentialClass = Type.OBJECT.equals(mergePotential) ? null : mergePotential;
- changed = true;
- }
- }
-
- Map merged;
-
- if (type instanceof MultiType) {
- MultiType multi = (MultiType)type;
-
- if (multi.resolved != null) {
- merged = mergeMultiAndSingle(this, multi.resolved);
- } else {
- merged = mergeMultiInterfaces(multi, this);
- if (! inMergeSource(multi))
- mergeSource = multi;
- }
- } else {
- merged = mergeMultiAndSingle(this, type);
- }
-
- // Keep all previous merge paths up to date
- if (merged.size() > 1 || (merged.size() == 1 && potentialClass != null)) {
- // Check for changes
- if (merged.size() != interfaces.size()) {
- changed = true;
- } else if (changed == false){
- Iterator iter = merged.keySet().iterator();
- while (iter.hasNext())
- if (! interfaces.containsKey(iter.next()))
- changed = true;
- }
-
- interfaces = merged;
- propogateState();
-
- return this;
- }
-
- if (merged.size() == 1) {
- resolved = Type.get((CtClass) merged.values().iterator().next());
- } else if (potentialClass != null){
- resolved = potentialClass;
- } else {
- resolved = OBJECT;
- }
-
- propogateResolved();
-
- return resolved;
- }
-
- public boolean equals(Object o) {
- if (! (o instanceof MultiType))
- return false;
-
- MultiType multi = (MultiType) o;
- if (resolved != null)
- return resolved.equals(multi.resolved);
- else if (multi.resolved != null)
- return false;
-
- return interfaces.keySet().equals(multi.interfaces.keySet());
- }
-
- public String toString() {
- if (resolved != null)
- return resolved.toString();
-
- StringBuffer buffer = new StringBuffer("{");
- Iterator iter = interfaces.keySet().iterator();
- while (iter.hasNext()) {
- buffer.append(iter.next());
- buffer.append(", ");
- }
- buffer.setLength(buffer.length() - 2);
- if (potentialClass != null)
- buffer.append(", *").append(potentialClass.toString());
- buffer.append("}");
- return buffer.toString();
- }
- }
|