blob: 31c1b85b9b29361eecb9bd0f1bd1c6190a832260 [file] [log] [blame]
/*
* 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;
}
}