/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * This file is available under and governed by the GNU General Public * License version 2 only, as published by the Free Software Foundation. * However, the following notice accompanied the original version of this * file: * * ASM: a very small and fast Java bytecode manipulation framework * Copyright (c) 2000-2011 INRIA, France Telecom * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of the copyright holders 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 jdk.internal.org.objectweb.asm.tree.analysis; import java.util.List; import jdk.internal.org.objectweb.asm.Type; /** * An extended {@link BasicVerifier} that performs more precise verifications. * This verifier computes exact class types, instead of using a single "object * reference" type (as done in the {@link BasicVerifier}). * * @author Eric Bruneton * @author Bing Ran */ public class SimpleVerifier extends BasicVerifier { /** * The class that is verified. */ private final Type currentClass; /** * The super class of the class that is verified. */ private final Type currentSuperClass; /** * The interfaces implemented by the class that is verified. */ private final List<Type> currentClassInterfaces; /** * If the class that is verified is an interface. */ private final boolean isInterface; /** * The loader to use for referenced classes. */ private ClassLoader loader = getClass().getClassLoader(); /** * Constructs a new {@link SimpleVerifier}. */ public SimpleVerifier() { this(null, null, false); } /** * Constructs a new {@link SimpleVerifier} to verify a specific class. This * class will not be loaded into the JVM since it may be incorrect. * * @param currentClass * the class that is verified. * @param currentSuperClass * the super class of the class that is verified. * @param isInterface * if the class that is verified is an interface. */ public SimpleVerifier(final Type currentClass, final Type currentSuperClass, final boolean isInterface) { this(currentClass, currentSuperClass, null, isInterface); } /** * Constructs a new {@link SimpleVerifier} to verify a specific class. This * class will not be loaded into the JVM since it may be incorrect. * * @param currentClass * the class that is verified. * @param currentSuperClass * the super class of the class that is verified. * @param currentClassInterfaces * the interfaces implemented by the class that is verified. * @param isInterface * if the class that is verified is an interface. */ public SimpleVerifier(final Type currentClass, final Type currentSuperClass, final List<Type> currentClassInterfaces, final boolean isInterface) { this(ASM5, currentClass, currentSuperClass, currentClassInterfaces, isInterface); } protected SimpleVerifier(final int api, final Type currentClass, final Type currentSuperClass, final List<Type> currentClassInterfaces, final boolean isInterface) { super(api); this.currentClass = currentClass; this.currentSuperClass = currentSuperClass; this.currentClassInterfaces = currentClassInterfaces; this.isInterface = isInterface; } /** * Set the <code>ClassLoader</code> which will be used to load referenced * classes. This is useful if you are verifying multiple interdependent * classes. * * @param loader * a <code>ClassLoader</code> to use */ public void setClassLoader(final ClassLoader loader) { this.loader = loader; } @Override public BasicValue newValue(final Type type) { if (type == null) { return BasicValue.UNINITIALIZED_VALUE; } boolean isArray = type.getSort() == Type.ARRAY; if (isArray) { switch (type.getElementType().getSort()) { case Type.BOOLEAN: case Type.CHAR: case Type.BYTE: case Type.SHORT: return new BasicValue(type); } } BasicValue v = super.newValue(type); if (BasicValue.REFERENCE_VALUE.equals(v)) { if (isArray) { v = newValue(type.getElementType()); String desc = v.getType().getDescriptor(); for (int i = 0; i < type.getDimensions(); ++i) { desc = '[' + desc; } v = new BasicValue(Type.getType(desc)); } else { v = new BasicValue(type); } } return v; } @Override protected boolean isArrayValue(final BasicValue value) { Type t = value.getType(); return t != null && ("Lnull;".equals(t.getDescriptor()) || t.getSort() == Type.ARRAY); } @Override protected BasicValue getElementValue(final BasicValue objectArrayValue) throws AnalyzerException { Type arrayType = objectArrayValue.getType(); if (arrayType != null) { if (arrayType.getSort() == Type.ARRAY) { return newValue(Type.getType(arrayType.getDescriptor() .substring(1))); } else if ("Lnull;".equals(arrayType.getDescriptor())) { return objectArrayValue; } } throw new Error("Internal error"); } @Override protected boolean isSubTypeOf(final BasicValue value, final BasicValue expected) { Type expectedType = expected.getType(); Type type = value.getType(); switch (expectedType.getSort()) { case Type.INT: case Type.FLOAT: case Type.LONG: case Type.DOUBLE: return type.equals(expectedType); case Type.ARRAY: case Type.OBJECT: if ("Lnull;".equals(type.getDescriptor())) { return true; } else if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { return isAssignableFrom(expectedType, type); } else { return false; } default: throw new Error("Internal error"); } } @Override public BasicValue merge(final BasicValue v, final BasicValue w) { if (!v.equals(w)) { Type t = v.getType(); Type u = w.getType(); if (t != null && (t.getSort() == Type.OBJECT || t.getSort() == Type.ARRAY)) { if (u != null && (u.getSort() == Type.OBJECT || u.getSort() == Type.ARRAY)) { if ("Lnull;".equals(t.getDescriptor())) { return w; } if ("Lnull;".equals(u.getDescriptor())) { return v; } if (isAssignableFrom(t, u)) { return v; } if (isAssignableFrom(u, t)) { return w; } // TODO case of array classes of the same dimension // TODO should we look also for a common super interface? // problem: there may be several possible common super // interfaces do { if (t == null || isInterface(t)) { return BasicValue.REFERENCE_VALUE; } t = getSuperClass(t); if (isAssignableFrom(t, u)) { return newValue(t); } } while (true); } } return BasicValue.UNINITIALIZED_VALUE; } return v; } protected boolean isInterface(final Type t) { if (currentClass != null && t.equals(currentClass)) { return isInterface; } return getClass(t).isInterface(); } protected Type getSuperClass(final Type t) { if (currentClass != null && t.equals(currentClass)) { return currentSuperClass; } Class<?> c = getClass(t).getSuperclass(); return c == null ? null : Type.getType(c); } protected boolean isAssignableFrom(final Type t, final Type u) { if (t.equals(u)) { return true; } if (currentClass != null && t.equals(currentClass)) { if (getSuperClass(u) == null) { return false; } else { if (isInterface) { return u.getSort() == Type.OBJECT || u.getSort() == Type.ARRAY; } return isAssignableFrom(t, getSuperClass(u)); } } if (currentClass != null && u.equals(currentClass)) { if (isAssignableFrom(t, currentSuperClass)) { return true; } if (currentClassInterfaces != null) { for (int i = 0; i < currentClassInterfaces.size(); ++i) { Type v = currentClassInterfaces.get(i); if (isAssignableFrom(t, v)) { return true; } } } return false; } Class<?> tc = getClass(t); if (tc.isInterface()) { tc = Object.class; } return tc.isAssignableFrom(getClass(u)); } protected Class<?> getClass(final Type t) { try { if (t.getSort() == Type.ARRAY) { return Class.forName(t.getDescriptor().replace('/', '.'), false, loader); } return Class.forName(t.getClassName(), false, loader); } catch (ClassNotFoundException e) { throw new RuntimeException(e.toString()); } } }