diff -r 000000000000 -r 959103a6100f test/tools/javac/lambdaShapes/org/openjdk/tests/separate/TestHarness.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/lambdaShapes/org/openjdk/tests/separate/TestHarness.java Wed Apr 27 01:34:52 2016 +0800 @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2012, 2013, 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. 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. + */ + +package org.openjdk.tests.separate; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import static org.openjdk.tests.separate.SourceModel.Class; +import static org.openjdk.tests.separate.SourceModel.*; +import static org.testng.Assert.*; + +public class TestHarness { + + /** + * Creates a per-thread persistent compiler object to allow as much + * sharing as possible, but still allows for parallel execution of tests. + */ + protected ThreadLocal compilerLocal = new ThreadLocal(){ + protected synchronized Compiler initialValue() { + return new Compiler(); + } + }; + + protected ThreadLocal verboseLocal = new ThreadLocal() { + protected synchronized Boolean initialValue() { + return Boolean.FALSE; + } + }; + + protected boolean verbose; + protected boolean canUseCompilerCache; + public static final String stdMethodName = SourceModel.stdMethodName; + + private TestHarness() { + } + + protected TestHarness(boolean verbose, boolean canUseCompilerCache) { + this.verbose = verbose; + this.canUseCompilerCache = canUseCompilerCache; + } + + public void setTestVerbose() { + verboseLocal.set(Boolean.TRUE); + } + + @AfterMethod + public void reset() { + if (!this.verbose) { + verboseLocal.set(Boolean.FALSE); + } + } + + public Compiler.Flags[] compilerFlags() { + HashSet flags = new HashSet<>(); + if (verboseLocal.get() == Boolean.TRUE) { + flags.add(Compiler.Flags.VERBOSE); + } + if (this.canUseCompilerCache) { + flags.add(Compiler.Flags.USECACHE); + } + return flags.toArray(new Compiler.Flags[0]); + } + + @AfterMethod + public void printError(ITestResult result) { + if (result.getStatus() == ITestResult.FAILURE) { + String clsName = result.getTestClass().getName(); + clsName = clsName.substring(clsName.lastIndexOf(".") + 1); + System.out.println("Test " + clsName + "." + + result.getName() + " FAILED"); + } + } + + private static final ConcreteMethod stdCM = ConcreteMethod.std("-1"); + private static final AbstractMethod stdAM = + new AbstractMethod("int", stdMethodName); + + /** + * Returns a class which has a static method with the same name as + * 'method', whose body creates an new instance of 'specimen' and invokes + * 'method' upon it via an invokevirtual instruction with 'args' as + * function call parameters. + * + * 'returns' is a dummy return value that need only match 'methods' + * return type (it is only used in the dummy class when compiling IV). + */ + private Class invokeVirtualHarness( + Class specimen, ConcreteMethod method, + String returns, String ... args) { + Method cm = new ConcreteMethod( + method.getReturnType(), method.getName(), + "return " + returns + ";", method.getElements()); + Class stub = new Class(specimen.getName(), cm); + + String params = toJoinedString(args, ", "); + + ConcreteMethod sm = new ConcreteMethod( + method.getReturnType(), method.getName(), + String.format("return (new %s()).%s(%s);", + specimen.getName(), method.getName(), params), + new AccessFlag("public"), new AccessFlag("static")); + + Class iv = new Class("IV_" + specimen.getName(), sm); + + iv.addCompilationDependency(stub); + iv.addCompilationDependency(cm); + + return iv; + } + + /** + * Returns a class which has a static method with the same name as + * 'method', whose body creates an new instance of 'specimen', casts it + * to 'iface' (including the type parameters) and invokes + * 'method' upon it via an invokeinterface instruction with 'args' as + * function call parameters. + */ + private Class invokeInterfaceHarness(Class specimen, Extends iface, + AbstractMethod method, String ... args) { + Interface istub = new Interface( + iface.getType().getName(), iface.getType().getAccessFlags(), + iface.getType().getParameters(), + null, Arrays.asList((Method)method)); + Class cstub = new Class(specimen.getName()); + + String params = toJoinedString(args, ", "); + + ConcreteMethod sm = new ConcreteMethod( + "int", SourceModel.stdMethodName, + String.format("return ((%s)(new %s())).%s(%s);", iface.toString(), + specimen.getName(), method.getName(), params), + new AccessFlag("public"), new AccessFlag("static")); + sm.suppressWarnings(); + + Class ii = new Class("II_" + specimen.getName() + "_" + + iface.getType().getName(), sm); + ii.addCompilationDependency(istub); + ii.addCompilationDependency(cstub); + ii.addCompilationDependency(method); + return ii; + } + + + /** + * Uses 'loader' to load class 'clzz', and calls the static method + * 'method'. If the return value does not equal 'value' (or if an + * exception is thrown), then a test failure is indicated. + * + * If 'value' is null, then no equality check is performed -- the assertion + * fails only if an exception is thrown. + */ + protected void assertStaticCallEquals( + ClassLoader loader, Class clzz, String method, Object value) { + java.lang.Class cls = null; + try { + cls = java.lang.Class.forName(clzz.getName(), true, loader); + } catch (ClassNotFoundException e) {} + assertNotNull(cls); + + java.lang.reflect.Method m = null; + try { + m = cls.getMethod(method); + } catch (NoSuchMethodException e) {} + assertNotNull(m); + + try { + Object res = m.invoke(null); + assertNotNull(res); + if (value != null) { + assertEquals(res, value); + } + } catch (InvocationTargetException | IllegalAccessException e) { + fail("Unexpected exception thrown: " + e.getCause(), e.getCause()); + } + } + + /** + * Creates a class which calls target::method(args) via invokevirtual, + * compiles and loads both the new class and 'target', and then invokes + * the method. If the returned value does not match 'value' then a + * test failure is indicated. + */ + public void assertInvokeVirtualEquals( + Object value, Class target, ConcreteMethod method, + String returns, String ... args) { + + Compiler compiler = compilerLocal.get(); + compiler.setFlags(compilerFlags()); + + Class iv = invokeVirtualHarness(target, method, returns, args); + ClassLoader loader = compiler.compile(iv, target); + + assertStaticCallEquals(loader, iv, method.getName(), value); + compiler.cleanup(); + } + + /** + * Convenience method for above, which assumes stdMethodName, + * a return type of 'int', and no arguments. + */ + public void assertInvokeVirtualEquals(int value, Class target) { + assertInvokeVirtualEquals(value, target, stdCM, "-1"); + } + + /** + * Creates a class which calls target::method(args) via invokeinterface + * through 'iface', compiles and loads both it and 'target', and + * then invokes the method. If the returned value does not match + * 'value' then a test failure is indicated. + */ + public void assertInvokeInterfaceEquals(Object value, Class target, + Extends iface, AbstractMethod method, String ... args) { + + Compiler compiler = compilerLocal.get(); + compiler.setFlags(compilerFlags()); + + Class ii = invokeInterfaceHarness(target, iface, method, args); + ClassLoader loader = compiler.compile(ii, target); + + assertStaticCallEquals(loader, ii, method.getName(), value); + compiler.cleanup(); + } + + /** + * Convenience method for above, which assumes stdMethodName, + * a return type of 'int', and no arguments. + */ + public void assertInvokeInterfaceEquals( + int value, Class target, Interface iface) { + + Compiler compiler = compilerLocal.get(); + compiler.setFlags(compilerFlags()); + + assertInvokeInterfaceEquals(value, target, new Extends(iface), stdAM); + + compiler.cleanup(); + } + + protected void assertInvokeInterfaceThrows(java.lang.Class errorClass, + Class target, Extends iface, AbstractMethod method, + String... args) { + try { + assertInvokeInterfaceEquals(0, target, iface, method, args); + fail("Expected exception: " + errorClass); + } + catch (AssertionError e) { + Throwable cause = e.getCause(); + if (cause == null) + throw e; + else if ((errorClass.isAssignableFrom(cause.getClass()))) { + // this is success + return; + } + else + throw e; + } + } + + /** + * Creates a class which calls target::method(args) via invokevirtual, + * compiles and loads both the new class and 'target', and then invokes + * the method. If an exception of type 'exceptionType' is not thrown, + * then a test failure is indicated. + */ + public void assertThrows(java.lang.Class exceptionType, Class target, + ConcreteMethod method, String returns, String ... args) { + + Compiler compiler = compilerLocal.get(); + compiler.setFlags(compilerFlags()); + + Class iv = invokeVirtualHarness(target, method, returns, args); + ClassLoader loader = compiler.compile(iv, target); + + java.lang.Class cls = null; + try { + cls = java.lang.Class.forName(iv.getName(), true, loader); + } catch (ClassNotFoundException e) {} + assertNotNull(cls); + + java.lang.reflect.Method m = null; + try { + m = cls.getMethod(method.getName()); + } catch (NoSuchMethodException e) {} + assertNotNull(m); + + try { + m.invoke(null); + fail("Exception should have been thrown"); + } catch (InvocationTargetException | IllegalAccessException e) { + if (verboseLocal.get() == Boolean.TRUE) { + System.out.println(e.getCause()); + } + assertTrue(exceptionType.isAssignableFrom(e.getCause().getClass())); + } + compiler.cleanup(); + } + + /** + * Convenience method for above, which assumes stdMethodName, + * a return type of 'int', and no arguments. + */ + public void assertThrows(java.lang.Class exceptionType, Class target) { + assertThrows(exceptionType, target, stdCM, "-1"); + } + + private static String toJoinedString(T[] a, String... p) { + return toJoinedString(Arrays.asList(a), p); + } + + private static String toJoinedString(List list, String... p) { + StringBuilder sb = new StringBuilder(); + String sep = ""; + String init = ""; + String end = ""; + String empty = null; + switch (p.length) { + case 4: + empty = p[3]; + /*fall-through*/ + case 3: + end = p[2]; + /*fall-through*/ + case 2: + init = p[1]; + /*fall-through*/ + case 1: + sep = p[0]; + break; + } + if (empty != null && list.isEmpty()) { + return empty; + } else { + sb.append(init); + for (T x : list) { + if (sb.length() != init.length()) { + sb.append(sep); + } + sb.append(x.toString()); + } + sb.append(end); + return sb.toString(); + } + } +}