Path: blob/master/test/jdk/java/lang/String/concat/StringConcatFactoryInvariants.java
41153 views
/*1* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.Serializable;24import java.lang.invoke.*;25import java.util.concurrent.Callable;2627/**28* @test29* @summary Test input invariants for StringConcatFactory30* @bug 8246152 824768131*32* @compile StringConcatFactoryInvariants.java33*34* @run main/othervm -Xverify:all StringConcatFactoryInvariants35*36*/37public class StringConcatFactoryInvariants {3839private static final char TAG_ARG = '\u0001';40private static final char TAG_CONST = '\u0002';4142public static void main(String[] args) throws Throwable {43MethodHandles.Lookup lookup = MethodHandles.lookup();44String methodName = "foo";45MethodType mt = MethodType.methodType(String.class, String.class, int.class);46String recipe = "" + TAG_ARG + TAG_ARG + TAG_CONST;47Object[][] constants = new Object[][] {48new String[] { "" },49new String[] { "bar" },50new Integer[] { 1 },51new Short[] { 2 },52new Long[] { 3L },53new Boolean[] { true },54new Character[] { 'a' },55new Byte[] { -128 },56new Class[] { String.class },57new MethodHandle[] { MethodHandles.constant(String.class, "constant") },58new MethodType[] { MethodType.methodType(String.class) }59};60// The string representation that should end up if the corresponding61// Object[] in constants is used as an argument to makeConcatWithConstants62String[] constantString = new String[] {63"",64"bar",65"1",66"2",67"3",68"true",69"a",70"-128",71"class java.lang.String",72"MethodHandle()String",73"()String"74};757677final int LIMIT = 200;7879// Simple factory: check for dynamic arguments overflow80Class<?>[] underThreshold = new Class<?>[LIMIT - 1];81Class<?>[] threshold = new Class<?>[LIMIT];82Class<?>[] overThreshold = new Class<?>[LIMIT + 1];8384StringBuilder sbUnderThreshold = new StringBuilder();85sbUnderThreshold.append(TAG_CONST);86for (int c = 0; c < LIMIT - 1; c++) {87underThreshold[c] = int.class;88threshold[c] = int.class;89overThreshold[c] = int.class;90sbUnderThreshold.append(TAG_ARG);91}92threshold[LIMIT - 1] = int.class;93overThreshold[LIMIT - 1] = int.class;94overThreshold[LIMIT] = int.class;9596String recipeEmpty = "";97String recipeUnderThreshold = sbUnderThreshold.toString();98String recipeThreshold = sbUnderThreshold.append(TAG_ARG).toString();99String recipeOverThreshold = sbUnderThreshold.append(TAG_ARG).toString();100101MethodType mtEmpty = MethodType.methodType(String.class);102MethodType mtUnderThreshold = MethodType.methodType(String.class, underThreshold);103MethodType mtThreshold = MethodType.methodType(String.class, threshold);104MethodType mtOverThreshold = MethodType.methodType(String.class, overThreshold);105106107// Check the basic functionality is working108{109CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, mt);110test("foo42", (String) cs.getTarget().invokeExact("foo", 42));111}112113{114for (int i = 0; i < constants.length; i++) {115CallSite cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, constants[i]);116test("foo42".concat(constantString[i]), (String) cs.getTarget().invokeExact("foo", 42));117}118}119120// Check unary expressions with pre- and postfix constants121{122String constArgRecipe = "" + TAG_CONST + TAG_ARG;123String argConstRecipe = "" + TAG_ARG + TAG_CONST;124MethodType unaryMt = MethodType.methodType(String.class, String.class);125126for (int i = 0; i < constants.length; i++) {127CallSite prefixCS = StringConcatFactory.makeConcatWithConstants(lookup, methodName, unaryMt, constArgRecipe, constants[i]);128test(constantString[i].concat("foo"), (String) prefixCS.getTarget().invokeExact("foo"));129130CallSite postfixCS = StringConcatFactory.makeConcatWithConstants(lookup, methodName, unaryMt, argConstRecipe, constants[i]);131test("foo".concat(constantString[i]), (String) postfixCS.getTarget().invokeExact("foo"));132}133}134135// Simple factory, check for nulls:136failNPE("Lookup is null",137() -> StringConcatFactory.makeConcat(null, methodName, mt));138139failNPE("Method name is null",140() -> StringConcatFactory.makeConcat(lookup, null, mt));141142failNPE("MethodType is null",143() -> StringConcatFactory.makeConcat(lookup, methodName, null));144145// Advanced factory, check for nulls:146for (int i = 0; i < constants.length; i++) {147final Object[] consts = constants[i];148149failNPE("Lookup is null",150() -> StringConcatFactory.makeConcatWithConstants(null, methodName, mt, recipe, consts));151152failNPE("Method name is null",153() -> StringConcatFactory.makeConcatWithConstants(lookup, null, mt, recipe, consts));154155failNPE("MethodType is null",156() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, null, recipe, consts));157158failNPE("Recipe is null",159() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, null, consts));160}161162failNPE("Constants vararg is null",163() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, (Object[]) null));164165failNPE("Constant argument is null",166() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mt, recipe, new Object[] { null }));167168// Simple factory, check for return type169fail("Return type: void",170() -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(void.class, String.class, int.class)));171172fail("Return type: int",173() -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(int.class, String.class, int.class)));174175fail("Return type: StringBuilder",176() -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class)));177178ok("Return type: Object",179() -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class)));180181ok("Return type: CharSequence",182() -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class)));183184ok("Return type: Serializable",185() -> StringConcatFactory.makeConcat(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class)));186187// Advanced factory, check for return types188for (int i = 0; i < constants.length; i++) {189final Object[] consts = constants[i];190fail("Return type: void",191() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(void.class, String.class, int.class), recipe, consts));192193fail("Return type: int",194() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(int.class, String.class, int.class), recipe, consts));195196fail("Return type: StringBuilder",197() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(StringBuilder.class, String.class, int.class), recipe, consts));198199ok("Return type: Object",200() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Object.class, String.class, int.class), recipe, consts));201202ok("Return type: CharSequence",203() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(CharSequence.class, String.class, int.class), recipe, consts));204205ok("Return type: Serializable",206() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(Serializable.class, String.class, int.class), recipe, consts));207}208209// Simple factory: check for dynamic arguments overflow210ok("Dynamic arguments is under limit",211() -> StringConcatFactory.makeConcat(lookup, methodName, mtUnderThreshold));212213ok("Dynamic arguments is at the limit",214() -> StringConcatFactory.makeConcat(lookup, methodName, mtThreshold));215216fail("Dynamic arguments is over the limit",217() -> StringConcatFactory.makeConcat(lookup, methodName, mtOverThreshold));218219// Advanced factory: check for dynamic arguments overflow220ok("Dynamic arguments is under limit",221() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtUnderThreshold, recipeUnderThreshold, constants[0]));222223ok("Dynamic arguments is at the limit",224() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants[0]));225226fail("Dynamic arguments is over the limit",227() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtOverThreshold, recipeOverThreshold, constants[0]));228229// Advanced factory: check for mismatched recipe and Constants230ok("Static arguments and recipe match",231() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar"));232233fail("Static arguments and recipe mismatch: too few",234() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold));235236fail("Static arguments and recipe mismatch: too many",237() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar", "baz"));238239failNPE("Static arguments and recipe mismatch, too many, overflowing constant is null",240() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, "bar", null));241242// Advanced factory: check for mismatched recipe and dynamic arguments243fail("Dynamic arguments and recipe mismatch",244() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeUnderThreshold, constants[0]));245246ok("Dynamic arguments and recipe match",247() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeThreshold, constants[0]));248249fail("Dynamic arguments and recipe mismatch",250() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtThreshold, recipeOverThreshold, constants[0]));251252// Test passing array as constant253{254Object[] arg = {"boo", "bar"};255256CallSite cs1 = StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST + TAG_CONST, arg);257test("42boobar", (String) cs1.getTarget().invokeExact(42));258}259260// Test passing null constant261ok("Can pass regular constants",262() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, "foo"));263264failNPE("Cannot pass null constants",265() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, MethodType.methodType(String.class, int.class), "" + TAG_ARG + TAG_CONST, new Object[]{null}));266267// Simple factory: test empty arguments268ok("Ok to pass empty arguments",269() -> StringConcatFactory.makeConcat(lookup, methodName, mtEmpty));270271// Advanced factory: test empty arguments272ok("Ok to pass empty arguments",273() -> StringConcatFactory.makeConcatWithConstants(lookup, methodName, mtEmpty, recipeEmpty));274275// Simple factory: public Lookup is rejected276fail("Passing public Lookup",277() -> StringConcatFactory.makeConcat(MethodHandles.publicLookup(), methodName, mtEmpty));278279// Advanced factory: public Lookup is rejected280fail("Passing public Lookup",281() -> StringConcatFactory.makeConcatWithConstants(MethodHandles.publicLookup(), methodName, mtEmpty, recipeEmpty));282283// Zero inputs284{285MethodType zero = MethodType.methodType(String.class);286CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, zero);287test("", (String) cs.getTarget().invokeExact());288289cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, zero, "");290test("", (String) cs.getTarget().invokeExact());291}292293// One input294{295MethodType zero = MethodType.methodType(String.class);296MethodType one = MethodType.methodType(String.class, String.class);297CallSite cs = StringConcatFactory.makeConcat(lookup, methodName, one);298test("A", (String) cs.getTarget().invokeExact("A"));299300cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, one, "\1");301test("A", (String) cs.getTarget().invokeExact("A"));302303cs = StringConcatFactory.makeConcatWithConstants(lookup, methodName, zero, "\2", "A");304test("A", (String) cs.getTarget().invokeExact());305}306}307308public static void ok(String msg, Callable runnable) {309try {310runnable.call();311} catch (Throwable e) {312e.printStackTrace();313throw new IllegalStateException(msg + ", should have passed", e);314}315}316317public static void fail(String msg, Callable runnable) {318boolean expected = false;319try {320runnable.call();321} catch (StringConcatException e) {322expected = true;323} catch (Throwable e) {324e.printStackTrace();325}326327if (!expected) {328throw new IllegalStateException(msg + ", should have failed with StringConcatException");329}330}331332333public static void failNPE(String msg, Callable runnable) {334boolean expected = false;335try {336runnable.call();337} catch (NullPointerException e) {338expected = true;339} catch (Throwable e) {340e.printStackTrace();341}342343if (!expected) {344throw new IllegalStateException(msg + ", should have failed with NullPointerException");345}346}347348public static void test(String expected, String actual) {349// Fingers crossed: String concat should work.350if (!expected.equals(actual)) {351StringBuilder sb = new StringBuilder();352sb.append("Expected = ");353sb.append(expected);354sb.append(", actual = ");355sb.append(actual);356throw new IllegalStateException(sb.toString());357}358}359360}361362363