001/***
002 * ASM: a very small and fast Java bytecode manipulation framework
003 * Copyright (c) 2000-2011 INRIA, France Telecom
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions
008 * are met:
009 * 1. Redistributions of source code must retain the above copyright
010 *    notice, this list of conditions and the following disclaimer.
011 * 2. Redistributions in binary form must reproduce the above copyright
012 *    notice, this list of conditions and the following disclaimer in the
013 *    documentation and/or other materials provided with the distribution.
014 * 3. Neither the name of the copyright holders nor the names of its
015 *    contributors may be used to endorse or promote products derived from
016 *    this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
028 * THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package io.ebean.enhance.asm.tree.analysis;
031
032import io.ebean.enhance.asm.Type;
033
034import java.util.List;
035
036/**
037 * An extended {@link BasicVerifier} that performs more precise verifications.
038 * This verifier computes exact class types, instead of using a single "object
039 * reference" type (as done in the {@link BasicVerifier}).
040 * 
041 * @author Eric Bruneton
042 * @author Bing Ran
043 */
044public class SimpleVerifier extends BasicVerifier {
045
046    /**
047     * The class that is verified.
048     */
049    private final Type currentClass;
050
051    /**
052     * The super class of the class that is verified.
053     */
054    private final Type currentSuperClass;
055
056    /**
057     * The interfaces implemented by the class that is verified.
058     */
059    private final List<Type> currentClassInterfaces;
060
061    /**
062     * If the class that is verified is an interface.
063     */
064    private final boolean isInterface;
065
066    /**
067     * The loader to use for referenced classes.
068     */
069    private ClassLoader loader = getClass().getClassLoader();
070
071    /**
072     * Constructs a new {@link SimpleVerifier}.
073     */
074    public SimpleVerifier() {
075        this(null, null, false);
076    }
077
078    /**
079     * Constructs a new {@link SimpleVerifier} to verify a specific class. This
080     * class will not be loaded into the JVM since it may be incorrect.
081     * 
082     * @param currentClass
083     *            the class that is verified.
084     * @param currentSuperClass
085     *            the super class of the class that is verified.
086     * @param isInterface
087     *            if the class that is verified is an interface.
088     */
089    public SimpleVerifier(final Type currentClass,
090                          final Type currentSuperClass, final boolean isInterface) {
091        this(currentClass, currentSuperClass, null, isInterface);
092    }
093
094    /**
095     * Constructs a new {@link SimpleVerifier} to verify a specific class. This
096     * class will not be loaded into the JVM since it may be incorrect.
097     * 
098     * @param currentClass
099     *            the class that is verified.
100     * @param currentSuperClass
101     *            the super class of the class that is verified.
102     * @param currentClassInterfaces
103     *            the interfaces implemented by the class that is verified.
104     * @param isInterface
105     *            if the class that is verified is an interface.
106     */
107    public SimpleVerifier(final Type currentClass,
108                          final Type currentSuperClass,
109                          final List<Type> currentClassInterfaces, final boolean isInterface) {
110        this(ASM6, currentClass, currentSuperClass, currentClassInterfaces,
111                isInterface);
112    }
113
114    protected SimpleVerifier(final int api, final Type currentClass,
115                             final Type currentSuperClass,
116                             final List<Type> currentClassInterfaces, final boolean isInterface) {
117        super(api);
118        this.currentClass = currentClass;
119        this.currentSuperClass = currentSuperClass;
120        this.currentClassInterfaces = currentClassInterfaces;
121        this.isInterface = isInterface;
122    }
123
124    /**
125     * Set the <code>ClassLoader</code> which will be used to load referenced
126     * classes. This is useful if you are verifying multiple interdependent
127     * classes.
128     * 
129     * @param loader
130     *            a <code>ClassLoader</code> to use
131     */
132    public void setClassLoader(final ClassLoader loader) {
133        this.loader = loader;
134    }
135
136    @Override
137    public BasicValue newValue(final Type type) {
138        if (type == null) {
139            return BasicValue.UNINITIALIZED_VALUE;
140        }
141
142        boolean isArray = type.getSort() == Type.ARRAY;
143        if (isArray) {
144            switch (type.getElementType().getSort()) {
145            case Type.BOOLEAN:
146            case Type.CHAR:
147            case Type.BYTE:
148            case Type.SHORT:
149                return new BasicValue(type);
150            }
151        }
152
153        BasicValue v = super.newValue(type);
154        if (BasicValue.REFERENCE_VALUE.equals(v)) {
155            if (isArray) {
156                v = newValue(type.getElementType());
157                String desc = v.getType().getDescriptor();
158                for (int i = 0; i < type.getDimensions(); ++i) {
159                    desc = '[' + desc;
160                }
161                v = new BasicValue(Type.getType(desc));
162            } else {
163                v = new BasicValue(type);
164            }
165        }
166        return v;
167    }
168
169    @Override
170    protected boolean isArrayValue(final BasicValue value) {
171        Type t = value.getType();
172        return t != null
173                && ("Lnull;".equals(t.getDescriptor()) || t.getSort() == Type.ARRAY);
174    }
175
176    @Override
177    protected BasicValue getElementValue(final BasicValue objectArrayValue)
178            throws AnalyzerException {
179        Type arrayType = objectArrayValue.getType();
180        if (arrayType != null) {
181            if (arrayType.getSort() == Type.ARRAY) {
182                return newValue(Type.getType(arrayType.getDescriptor()
183                        .substring(1)));
184            } else if ("Lnull;".equals(arrayType.getDescriptor())) {
185                return objectArrayValue;
186            }
187        }
188        throw new Error("Internal error");
189    }
190
191    @Override
192    protected boolean isSubTypeOf(final BasicValue value,
193            final BasicValue expected) {
194        Type expectedType = expected.getType();
195        Type type = value.getType();
196        switch (expectedType.getSort()) {
197        case Type.INT:
198        case Type.FLOAT:
199        case Type.LONG:
200        case Type.DOUBLE:
201            return type.equals(expectedType);
202        case Type.ARRAY:
203        case Type.OBJECT:
204            if ("Lnull;".equals(type.getDescriptor())) {
205                return true;
206            } else if (type.getSort() == Type.OBJECT
207                    || type.getSort() == Type.ARRAY) {
208                return isAssignableFrom(expectedType, type);
209            } else {
210                return false;
211            }
212        default:
213            throw new Error("Internal error");
214        }
215    }
216
217    @Override
218    public BasicValue merge(final BasicValue v, final BasicValue w) {
219        if (!v.equals(w)) {
220            Type t = v.getType();
221            Type u = w.getType();
222            if (t != null
223                    && (t.getSort() == Type.OBJECT || t.getSort() == Type.ARRAY)) {
224                if (u != null
225                        && (u.getSort() == Type.OBJECT || u.getSort() == Type.ARRAY)) {
226                    if ("Lnull;".equals(t.getDescriptor())) {
227                        return w;
228                    }
229                    if ("Lnull;".equals(u.getDescriptor())) {
230                        return v;
231                    }
232                    if (isAssignableFrom(t, u)) {
233                        return v;
234                    }
235                    if (isAssignableFrom(u, t)) {
236                        return w;
237                    }
238                    // TODO case of array classes of the same dimension
239                    // TODO should we look also for a common super interface?
240                    // problem: there may be several possible common super
241                    // interfaces
242                    do {
243                        if (t == null || isInterface(t)) {
244                            return BasicValue.REFERENCE_VALUE;
245                        }
246                        t = getSuperClass(t);
247                        if (isAssignableFrom(t, u)) {
248                            return newValue(t);
249                        }
250                    } while (true);
251                }
252            }
253            return BasicValue.UNINITIALIZED_VALUE;
254        }
255        return v;
256    }
257
258    protected boolean isInterface(final Type t) {
259        if (currentClass != null && t.equals(currentClass)) {
260            return isInterface;
261        }
262        return getClass(t).isInterface();
263    }
264
265    protected Type getSuperClass(final Type t) {
266        if (currentClass != null && t.equals(currentClass)) {
267            return currentSuperClass;
268        }
269        Class<?> c = getClass(t).getSuperclass();
270        return c == null ? null : Type.getType(c);
271    }
272
273    protected boolean isAssignableFrom(final Type t, final Type u) {
274        if (t.equals(u)) {
275            return true;
276        }
277        if (currentClass != null && t.equals(currentClass)) {
278            if (getSuperClass(u) == null) {
279                return false;
280            } else {
281                if (isInterface) {
282                    return u.getSort() == Type.OBJECT
283                            || u.getSort() == Type.ARRAY;
284                }
285                return isAssignableFrom(t, getSuperClass(u));
286            }
287        }
288        if (currentClass != null && u.equals(currentClass)) {
289            if (isAssignableFrom(t, currentSuperClass)) {
290                return true;
291            }
292            if (currentClassInterfaces != null) {
293                for (int i = 0; i < currentClassInterfaces.size(); ++i) {
294                    Type v = currentClassInterfaces.get(i);
295                    if (isAssignableFrom(t, v)) {
296                        return true;
297                    }
298                }
299            }
300            return false;
301        }
302        Class<?> tc = getClass(t);
303        if (tc.isInterface()) {
304            tc = Object.class;
305        }
306        return tc.isAssignableFrom(getClass(u));
307    }
308
309    protected Class<?> getClass(final Type t) {
310        try {
311            if (t.getSort() == Type.ARRAY) {
312                return Class.forName(t.getDescriptor().replace('/', '.'),
313                        false, loader);
314            }
315            return Class.forName(t.getClassName(), false, loader);
316        } catch (ClassNotFoundException e) {
317            throw new RuntimeException(e.toString());
318        }
319    }
320}