/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * 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. * * 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. */ package test.com.sun.max.vm.verifier; import static com.sun.max.vm.MaxineVM.*; import static com.sun.max.vm.hosted.HostedVMClassLoader.*; import java.io.*; import java.util.*; import junit.framework.*; import test.com.sun.max.vm.*; import test.com.sun.max.vm.bytecode.*; import com.sun.max.annotate.*; import com.sun.max.program.*; import com.sun.max.program.option.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.classfile.*; import com.sun.max.vm.hosted.*; import com.sun.max.vm.type.*; import com.sun.max.vm.verifier.*; /** * Tests for both the {@link TypeCheckingVerifier} and the {@link TypeInferencingVerifier}. * Unless program arguments are supplied, the tests simply verify all the methods in the * Java prototype. * * -jcklist=test/test/com/sun/max/vm/verifier/jck.classes.txt */ public class VerifierTest extends VmTestCase { private static final OptionSet options = new OptionSet(); private static final Option<String> CLASS = options.newStringOption("class", null, "This option specifies the Java class to verify."); private static final Option<String> CLASSES = options.newStringOption("list", null, "This option specifies the name of a file that contains the classes to test."); private static final Option<File> JCKCLASSES = options.newFileOption("jcklist", (File) null, "This option specifies the name of a file that lists JCK classes to test."); private static final Option<Policy> POLICY = options.newEnumOption("policy", Policy.standard, Policy.class, "This option specifies the verification policy to use."); private static final Option<Integer> FAILURES = options.newIntegerOption("k", 1, "This option specifies how many tests to allow to fail before terminating."); private static final Option<Boolean> VMCLASSES = options.newBooleanOption("vmclasses", false, "This option determines whether the verifier will attempt to verify all classes in the VM class registry."); private static int failedTestThreshold; public static void main(String[] args) { setProgramArguments(args); junit.textui.TestRunner.run(VerifierTest.suite()); } public static Test suite() { final TestSuite suite = new TestSuite(VerifierTest.class.getSimpleName()); suite.addTestSuite(VerifierTest.class); return new VmTestSetup(suite); } private enum Policy { standard, jsr202, dfa } private static synchronized void parseProgramArguments() { if (initialized) { return; } initialized = true; Trace.addTo(options); options.parseArguments(getProgramArguments()); setProgramArguments(options.getArgumentsAndUnrecognizedOptions().asArguments()); failedTestThreshold = FAILURES.getValue(); } public VerifierTest(String name) { super(name); parseProgramArguments(); } private static boolean initialized; List<String> readClassNames(File file) throws IOException { final List<String> lines = new ArrayList<String>(); final BufferedReader reader = new BufferedReader(new FileReader(file)); String line; while ((line = reader.readLine()) != null) { final String className = line.trim(); if (!line.startsWith("#") && line.length() > 0) { lines.add(className); } } return lines; } static class SomeTest { Map[] maps(int n, Object o) { Map[] result = new HashMap[0]; for (int i = 0; i < n; i++) { result = (Map[]) o; } return result; } } public void test() throws Exception { // TODO change to include VM_CLASS_REGISTRY verify(SomeTest.class.getName(), true); verify(JdtBadStackMapTable.class.getName(), true); int numberOfClassesVerified = 0; if (JsrInliningTestSource.compile()) { verify(JsrInliningTestSource.class.getName(), true); verify(JsrInliningTestSource.Unverifiable.class.getName(), false); numberOfClassesVerified += 2; } if (JCKCLASSES.getValue() != null) { Trace.line(1, "Running JCK test classes in " + JCKCLASSES.getValue().getAbsolutePath()); for (String className : readClassNames(JCKCLASSES.getValue())) { verify(className, !className.endsWith("n")); ++numberOfClassesVerified; } } if (CLASSES.getValue() != null) { for (String className : readClassNames(new File(CLASSES.getValue()))) { verify(className, true); ++numberOfClassesVerified; } } if (CLASS.getValue() != null) { verify(CLASS.getValue(), true); ++numberOfClassesVerified; } int classActorCount = 0; if (numberOfClassesVerified == 0 || VMCLASSES.getValue()) { while (true) { for (ClassActor classActor : ClassRegistry.allBootImageClasses()) { if (classActor.isTupleClass()) { final String name = classActor.name.toString(); if (!verifiedClasses.contains(name) && name.startsWith("com.") || name.startsWith("java.")) { verify(name, true); ++numberOfClassesVerified; } } classActorCount++; } // check for additions if (classActorCount == ClassRegistry.numberOfBootImageClassActors()) { break; } } } } private void verify(String name, boolean isPositive) { try { verify0(name, isPositive); } catch (RuntimeException runtimeException) { if (--failedTestThreshold > 0) { addTestError(runtimeException); } else { throw runtimeException; } } catch (Error error) { if (--failedTestThreshold > 0) { addTestError(error); } else { throw error; } } finally { Trace.stream().flush(); } } /** * Filter for certain methods that are known not to verify due to use of Word types or other special constructs. */ public static boolean suppressVerificationOf(ClassMethodActor method) { if (method.isTemplate() || method.isAbstract() || method.intrinsic() != null) { return true; } if (METHOD_SUBSTITUTIONS.Static.findSubstituteFor(method) != null) { // Suppress substituted methods return true; } if (method.holder().kind.isWord) { return true; } return false; } static class FilteringTypeCheckingVerifier extends TypeCheckingVerifier { public FilteringTypeCheckingVerifier(ClassActor classActor) { super(classActor); } @Override protected void verifyMethod(ClassMethodActor classMethodActor) { if (!suppressVerificationOf(classMethodActor)) { super.verifyMethod(classMethodActor); } } } static class FilteringTypeInferencingVerifier extends TypeInferencingVerifier { public FilteringTypeInferencingVerifier(ClassActor classActor) { super(classActor); } @Override protected void verifyMethod(ClassMethodActor classMethodActor) { if (!suppressVerificationOf(classMethodActor)) { super.verifyMethod(classMethodActor); } } } private static final Set<String> verifiedClasses = new HashSet<String>(); private void verify0(String name, boolean isPositive) { verifiedClasses.add(name); ClassVerifier classVerifier = null; final ClassfileVersion classfileVersion = new ClassfileVersion(name, HostedVMClassLoader.HOSTED_VM_CLASS_LOADER.classpath()); try { Trace.line(1, "verifying " + name); switch (POLICY.getValue()) { case jsr202: if (classfileVersion.major < 50) { return; } classVerifier = new FilteringTypeCheckingVerifier(loadClassActor(name)); break; case dfa: classVerifier = new FilteringTypeInferencingVerifier(loadClassActor(name)); break; case standard: ClassActor classActor = loadClassActor(name); final int majorVersion = classActor.majorVersion; if (majorVersion >= 50) { classVerifier = new FilteringTypeCheckingVerifier(classActor); } else { classVerifier = new FilteringTypeInferencingVerifier(classActor); } break; default: throw ProgramError.unexpected(); } classVerifier.verify(); if (!isPositive) { repeatVerificationToDiagnoseFailure(classVerifier, null); fail(name + " did not fail verification as expected"); } } catch (HostOnlyClassError e) { } catch (HostOnlyMethodError e) { } catch (HostOnlyFieldError e) { } catch (NoClassDefFoundError noClassDefFoundError) { if (isPositive) { throw noClassDefFoundError; } } catch (ClassFormatError classFormatError) { System.err.println(classFormatError); if (isPositive) { throw classFormatError; } } catch (VerifyError verifyError) { System.err.println(verifyError); if (isPositive) { if (verifyError instanceof ExtendedVerifyError) { repeatVerificationToDiagnoseFailure(classVerifier, (ExtendedVerifyError) verifyError); } throw verifyError; } } catch (AssertionFailedError assertionFailedError) { throw assertionFailedError; } catch (Throwable verifyError) { repeatVerificationToDiagnoseFailure(classVerifier, null); } } private void repeatVerificationToDiagnoseFailure(ClassVerifier classVerifier, ExtendedVerifyError extendedVerifyError) { // Only repeat the very last failure if (failedTestThreshold == 1) { if (classVerifier != null) { classVerifier.verbose = true; if (extendedVerifyError != null) { System.out.println(extendedVerifyError.getMessage()); extendedVerifyError.printCode(System.out); System.out.flush(); classVerifier.verify(extendedVerifyError.classMethodActor, extendedVerifyError.codeAttribute); } else { classVerifier.verify(); } } } } private ClassActor loadClassActor(String name) { final TypeDescriptor typeDescriptor = JavaTypeDescriptor.getDescriptorForJavaString(name); final HostedVMClassLoader vmClassLoader = HostedVMClassLoader.HOSTED_VM_CLASS_LOADER; final Classpath classpath = vmClassLoader.classpath(); ClassActor classActor = ClassRegistry.get(vmClassLoader, typeDescriptor, false); if (classActor == null) { final ClasspathFile classpathFile = classpath.readClassFile(name); if (classpathFile == null) { fail("Could not find class " + name + " on class path: " + classpath); } classActor = ClassfileReader.defineClassActor(name, vmClassLoader, classpathFile.contents, null, classpathFile.classpathEntry, true); } return classActor; } public void test_jsr() throws Exception { final MethodActor classMethodActor = new TestBytecodeAssembler(true, "PerformJsr", "perform_jsr", SignatureDescriptor.create(int.class)) { @Override public void generateCode() { final Label subroutine = newLabel(); final Label cont = newLabel(); final int entry = currentAddress(); final int retAddress = allocateLocal(Kind.REFERENCE); final int var = allocateLocal(Kind.INT); iconst(42); istore(var); jsr(subroutine); iload(var); ireturn(); subroutine.bind(); setStack(1); astore(retAddress); iconst(1); ifne(cont); aconst_null(); astore(var); goto_(entry); cont.bind(); ret(retAddress); } }.classMethodActor(Object.class); verify(classMethodActor.holder().name.toString(), true); assertTrue(classMethodActor.toJava().invoke(null).equals(42)); } /** * Tests that a subroutine with two different entry points causes a verification error. */ public void test_jsr2() throws Exception { final TestBytecodeAssembler asm = new TestBytecodeAssembler(true, "PerformJsr2", "perform_jsr2", SignatureDescriptor.create(int.class)) { @Override public void generateCode() { final Label subroutineEntry1 = newLabel(); final Label subroutineEntry2 = newLabel(); final int retAddress = allocateLocal(Kind.REFERENCE); final int var = allocateLocal(Kind.INT); iconst(42); istore(var); jsr(subroutineEntry1); jsr(subroutineEntry2); iload(var); ireturn(); subroutineEntry1.bind(); setStack(1); iconst(43); pop(); subroutineEntry2.bind(); astore(retAddress); iconst(44); pop(); ret(retAddress); } }; try { final MethodActor classMethodActor = asm.classMethodActor(Object.class); verify(classMethodActor.holder().name.toString(), false); } catch (VerifyError verifyError) { // success } } public static class Super { void callsPrivateMethodOnSubclassInstance() { final Super instance = new Super() {}; instance.privateMethod(); } private void privateMethod() { } } public void test_Super() { verify(Super.class.getName(), true); } public void test_max() { TypeCheckingVerifier.FailOverToOldVerifier = false; new ClassSearch() { @Override protected boolean visitClass(String className) { if ((className.startsWith("com.sun.max.vm") || className.startsWith("com.sun.c1x")) && !className.endsWith("package-info") && isBootImageClass(className)) { try { Class< ? > c = Class.forName(className, false, HOSTED_VM_CLASS_LOADER); if (!isHostedOnly(c)) { verify(className, true); } } catch (ClassNotFoundException e) { } catch (NoClassDefFoundError e) { } } return true; } }.run(Classpath.fromSystem()); } }