/* * Copyright (c) 2013, 2018, 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 vm.runtime.defmeth.shared.builder; import nsk.share.Pair; import jdk.internal.org.objectweb.asm.Opcodes; import vm.runtime.defmeth.shared.Util; import vm.runtime.defmeth.shared.data.Clazz; import vm.runtime.defmeth.shared.data.ConcreteClass; import vm.runtime.defmeth.shared.data.Tester; import vm.runtime.defmeth.shared.data.method.Method; import vm.runtime.defmeth.shared.data.method.param.IntParam; import vm.runtime.defmeth.shared.data.method.param.Param; import vm.runtime.defmeth.shared.data.method.param.StringParam; import vm.runtime.defmeth.shared.data.method.result.IntResult; import vm.runtime.defmeth.shared.data.method.result.Result; import vm.runtime.defmeth.shared.data.method.result.ResultIgnore; import vm.runtime.defmeth.shared.data.method.result.ThrowExResult; import static jdk.internal.org.objectweb.asm.Opcodes.*; import vm.runtime.defmeth.shared.data.Interface; import vm.runtime.defmeth.shared.data.method.body.CallMethod; import vm.runtime.defmeth.shared.data.method.body.CallMethod.Invoke; /** * Builder for data.Tester instances. * * Simplifies single test construction during test case preparation. * Test scenario is the following: call some method and check it's result. */ public class TesterBuilder implements Builder { // Test number private final int id; // Test class name private String name; // Static receiver of the call private Clazz staticReceiver; // Dynamic receiver of the call // Null, if calling static method. private ConcreteClass dynamicReceiver; // Method to call: name + erased signature private Method m; // Expected result of method invocation private Result result; // Parameters for method invocation private Param[] params = new Param[0]; // Enclosing test builder private TestBuilder builder; // private method test private boolean testPrivateMethod; // Type of constant pool entry used at call site private CallMethod.IndexbyteOp cpEntryType = CallMethod.IndexbyteOp.CALLSITE; // Callee-specific by default /* package-private */ TesterBuilder(int id, TestBuilder builder) { this.id = id; this.builder = builder; this.testPrivateMethod = false; } public TesterBuilder name(String name) { this.name = name; return this; } private TesterBuilder static_(Clazz receiver) { staticReceiver = receiver; return this; } private TesterBuilder dynamic(ConcreteClass receiver) { dynamicReceiver = receiver; return this; } public TesterBuilder callee(Method m) { this.m = m; return this; } public TesterBuilder callee(String name, String desc) { return callee( builder.method().name(name).desc(desc).build()); } public TesterBuilder callee(String name, String desc, int acc) { return callee( builder.method().name(name).desc(desc).flags(acc).build()); } public TesterBuilder cpEntryType(CallMethod.IndexbyteOp type) { cpEntryType = type; return this; } public TesterBuilder callSite(Clazz staticReceiver, ConcreteClass receiver, String methodName, String methodDesc) { return static_(staticReceiver) .dynamic(receiver) .callee(methodName, methodDesc); } public TesterBuilder callSite(ConcreteClass receiver, String methodName, String methodDesc) { return dynamic(receiver).callee(methodName, methodDesc); } public TesterBuilder staticCallSite(Clazz I, String methodName, String methodDesc) { return static_(I).callee(methodName, methodDesc, ACC_STATIC); } public TesterBuilder privateCallSite(Clazz staticReceiver, ConcreteClass receiver, String methodName, String methodDesc) { this.testPrivateMethod = true; return static_(staticReceiver) .dynamic(receiver) .callee(methodName, methodDesc); } public TesterBuilder interfaceCallSite(Clazz staticReceiver, ConcreteClass receiver, String methodName, String methodDesc) { return static_(staticReceiver) .dynamic(receiver) .callee(methodName, methodDesc, ACC_INTERFACE) .cpEntryType(CallMethod.IndexbyteOp.INTERFACEMETHODREF); } public TesterBuilder params(int... intParams) { this.params = new Param[intParams.length]; for (int i = 0; i < intParams.length; i++) { this.params[i] = new IntParam(intParams[i]); } return this; } public TesterBuilder params(String... strParams) { this.params = new Param[strParams.length]; for (int i = 0; i < strParams.length; i++) { this.params[i] = new StringParam(strParams[i]); } return this; } public TesterBuilder params(Param... params) { this.params = params; return this; } public TesterBuilder result(Result result) { this.result = result; return this; } public TesterBuilder ignoreResult() { return result(new ResultIgnore());} public TesterBuilder returns(int i) { return result(new IntResult(i)); } public TesterBuilder throws_(Clazz clz) { return result(new ThrowExResult(clz, false, null)); } public TesterBuilder throws_(Class exc) { return result(new ThrowExResult(builder.toClazz(exc), false, null)); } public TesterBuilder throwsExact(Clazz clz) { return result(new ThrowExResult(clz, true, null)); } public TesterBuilder throwsExact(Class exc) { return result(new ThrowExResult(builder.toClazz(exc), true, null)); } public TesterBuilder succeeds() { return result(new ResultIgnore()); } public TesterBuilder loadClass(Clazz clz) { return static_(builder.clazz("java.lang.Class").build()) .callee("forName", "(Ljava/lang/String;)Ljava/lang/Class;", ACC_STATIC) .params(clz.name()); } /** * Choose right instruction for a call depending on the callee type. * * @return */ private CallMethod.Invoke getCallInsn() { if ((m.acc() & Opcodes.ACC_STATIC) != 0) { return Invoke.STATIC; } else { if (staticReceiver instanceof Interface) { return Invoke.INTERFACE; } else if (staticReceiver instanceof ConcreteClass) { if ((m.acc() & Opcodes.ACC_INTERFACE) == 0) { return Invoke.VIRTUAL; } else { return Invoke.INTERFACE; } } else { throw new UnsupportedOperationException("Can't detect invoke instruction"); } } } @Override public Tester build() { if (staticReceiver == null) { throw new IllegalStateException(); } if (m == null) { throw new IllegalStateException(); } if (result == null) { throw new IllegalStateException(); } if (params.length == 0) { params = Util.getDefaultValues( Util.parseDesc(m.desc()).first); } if (name == null) { name = String.format("Test%d_%s_%s_%s", id, staticReceiver != null ? staticReceiver.getShortName() : "", dynamicReceiver != null ? dynamicReceiver.getShortName() : "", m.name()); } Invoke callInsn = getCallInsn(); Pair calleeDetails = Util.parseDesc(m.desc()); String returnType = calleeDetails.second; CallMethod call = new CallMethod(callInsn, staticReceiver, dynamicReceiver, m.name(), m.desc(), params, returnType, false, cpEntryType); Tester tester = new Tester(name, call, result, testPrivateMethod); builder.register(tester); // Notify test builder that test construction is finished builder.finishConstruction(this); return tester; } @Override public TestBuilder done() { build(); return builder; } }