| /* |
| * 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<Tester> { |
| // 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<? extends Throwable> 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<? extends Throwable> 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<String[],String> 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; |
| } |
| } |