| /* |
| * Copyright 2008 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.benchmarks.rebind; |
| |
| import com.google.gwt.benchmarks.BenchmarkShell; |
| import com.google.gwt.benchmarks.client.IterationTimeLimit; |
| import com.google.gwt.benchmarks.client.RangeEnum; |
| import com.google.gwt.benchmarks.client.RangeField; |
| import com.google.gwt.benchmarks.client.Setup; |
| import com.google.gwt.benchmarks.client.Teardown; |
| import com.google.gwt.benchmarks.client.impl.BenchmarkResults; |
| import com.google.gwt.benchmarks.client.impl.IterableAdapter; |
| import com.google.gwt.benchmarks.client.impl.PermutationIterator; |
| import com.google.gwt.benchmarks.client.impl.Trial; |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.core.ext.UnableToCompleteException; |
| import com.google.gwt.core.ext.typeinfo.JClassType; |
| import com.google.gwt.core.ext.typeinfo.JField; |
| import com.google.gwt.core.ext.typeinfo.JMethod; |
| import com.google.gwt.core.ext.typeinfo.JParameter; |
| import com.google.gwt.dev.generator.ast.ForLoop; |
| import com.google.gwt.dev.generator.ast.MethodCall; |
| import com.google.gwt.dev.generator.ast.Statement; |
| import com.google.gwt.dev.generator.ast.Statements; |
| import com.google.gwt.dev.generator.ast.StatementsList; |
| import com.google.gwt.junit.rebind.JUnitTestCaseStubGenerator; |
| import com.google.gwt.user.client.DeferredCommand; |
| import com.google.gwt.user.client.IncrementalCommand; |
| import com.google.gwt.user.rebind.SourceWriter; |
| |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Implements a generator for Benchmark classes. Benchmarks require additional |
| * code generation above and beyond standard JUnit tests. |
| */ |
| public class BenchmarkGenerator extends JUnitTestCaseStubGenerator { |
| |
| private static class MutableLong { |
| long value; |
| } |
| |
| private static final String BEGIN_PREFIX = "begin"; |
| |
| private static final String BENCHMARK_PARAM_META = "gwt.benchmark.param"; |
| |
| private static final String BENCHMARK_RESULTS_CLASS = BenchmarkResults.class.getName(); |
| |
| private static long defaultTimeout = -1; |
| |
| private static final String EMPTY_FUNC = "__emptyFunc"; |
| |
| private static final String END_PREFIX = "end"; |
| |
| private static final String ESCAPE_LOOP = "__escapeLoop"; |
| |
| private static final String ITERABLE_ADAPTER_CLASS = IterableAdapter.class.getName(); |
| |
| private static final String PERMUTATION_ITERATOR_CLASS = PermutationIterator.class.getName(); |
| |
| private static final String TRIAL_CLASS = Trial.class.getName(); |
| |
| /** |
| * Returns all the zero-argument JUnit test methods that do not have |
| * overloads. |
| * |
| * @return Map<String,JMethod> |
| */ |
| public static Map<String, JMethod> getNotOverloadedTestMethods( |
| JClassType requestedClass) { |
| Map<String, List<JMethod>> methods = getAllMethods(requestedClass, |
| new MethodFilter() { |
| public boolean accept(JMethod method) { |
| return isJUnitTestMethod(method, true); |
| } |
| }); |
| |
| // Create a new map to store the methods |
| Map<String, JMethod> notOverloadedMethods = new HashMap<String, JMethod>(); |
| for (Map.Entry<String, List<JMethod>> entry : methods.entrySet()) { |
| List<JMethod> methodOverloads = entry.getValue(); |
| if (methodOverloads.size() == 1) { |
| JMethod overload = methodOverloads.get(0); |
| if (overload.getParameters().length == 0) { |
| notOverloadedMethods.put(entry.getKey(), overload); |
| } |
| } |
| } |
| |
| return notOverloadedMethods; |
| } |
| |
| /** |
| * Returns all the JUnit test methods that are overloaded test methods with |
| * parameters. Does not include the zero-argument test methods. |
| * |
| * @return Map<String,JMethod> |
| */ |
| public static Map<String, JMethod> getParameterizedTestMethods( |
| JClassType requestedClass, TreeLogger logger) { |
| Map<String, List<JMethod>> testMethods = getAllMethods(requestedClass, |
| new MethodFilter() { |
| public boolean accept(JMethod method) { |
| return isJUnitTestMethod(method, true); |
| } |
| }); |
| |
| // Create a new mapping to return |
| Map<String, JMethod> overloadedMethods = new HashMap<String, JMethod>(); |
| |
| // Remove all non-overloaded test methods |
| for (Map.Entry<String, List<JMethod>> entry : testMethods.entrySet()) { |
| String name = entry.getKey(); |
| List<JMethod> methods = entry.getValue(); |
| |
| if (methods.size() > 2) { |
| String msg = requestedClass + "." + name |
| + " has more than one overloaded version" |
| + "; it will not be included in the test case execution"; |
| logger.log(TreeLogger.WARN, msg, null); |
| continue; |
| } |
| |
| if (methods.size() == 1) { |
| JMethod method = methods.get(0); |
| if (method.getParameters().length != 0) { |
| /* |
| * User probably goofed - otherwise why create a test method with |
| * arguments but not the corresponding no-argument version? Would be |
| * better if our benchmarking system didn't require the no-argument |
| * test to make the benchmarks run correctly (JUnit artifact). |
| */ |
| String msg = requestedClass + "." + name |
| + " does not have a zero-argument overload" |
| + "; it will not be included in the test case execution"; |
| logger.log(TreeLogger.WARN, msg, null); |
| } |
| // Only a zero-argument version, we don't need to process it. |
| continue; |
| } |
| |
| JMethod method1 = methods.get(0); |
| JMethod method2 = methods.get(1); |
| JMethod noArgMethod = null; |
| JMethod overloadedMethod = null; |
| |
| if (method1.getParameters().length == 0) { |
| noArgMethod = method1; |
| } else { |
| overloadedMethod = method1; |
| } |
| |
| if (method2.getParameters().length == 0) { |
| noArgMethod = method2; |
| } else { |
| overloadedMethod = method2; |
| } |
| |
| if (noArgMethod == null) { |
| String msg = requestedClass + "." + name |
| + " does not have a zero-argument overload" |
| + "; it will not be included in the test case execution"; |
| logger.log(TreeLogger.WARN, msg, null); |
| continue; |
| } |
| |
| overloadedMethods.put(entry.getKey(), overloadedMethod); |
| } |
| |
| return overloadedMethods; |
| } |
| |
| private static JMethod getBeginMethod(JClassType type, JMethod method) { |
| Setup setup = method.getAnnotation(Setup.class); |
| String methodName; |
| if (setup != null) { |
| methodName = setup.value(); |
| } else { |
| methodName = new StringBuffer(method.getName()).replace(0, |
| "test".length(), BEGIN_PREFIX).toString(); |
| } |
| return getMethod(type, methodName); |
| } |
| |
| private static JMethod getEndMethod(JClassType type, JMethod method) { |
| Teardown teardown = method.getAnnotation(Teardown.class); |
| String methodName; |
| if (teardown != null) { |
| methodName = teardown.value(); |
| } else { |
| methodName = new StringBuffer(method.getName()).replace(0, |
| "test".length(), END_PREFIX).toString(); |
| } |
| return getMethod(type, methodName); |
| } |
| |
| private static JMethod getMethod(JClassType type, MethodFilter filter) { |
| Map<String, List<JMethod>> map = getAllMethods(type, filter); |
| Set<Map.Entry<String, List<JMethod>>> entrySet = map.entrySet(); |
| if (entrySet.size() == 0) { |
| return null; |
| } |
| List<JMethod> methods = entrySet.iterator().next().getValue(); |
| return methods.get(0); |
| } |
| |
| private static JMethod getMethod(JClassType type, final String name) { |
| return getMethod(type, new MethodFilter() { |
| public boolean accept(JMethod method) { |
| return method.getName().equals(name); |
| } |
| }); |
| } |
| |
| @Override |
| public void writeSource() throws UnableToCompleteException { |
| super.writeSource(); |
| |
| generateEmptyFunc(getSourceWriter()); |
| implementZeroArgTestMethods(); |
| implementParameterizedTestMethods(); |
| generateAsyncCode(); |
| BenchmarkShell.getReport().addBenchmark(logger, getRequestedClass()); |
| } |
| |
| /** |
| * Generates benchmarking code which wraps <code>stmts</code> The timing |
| * result is a double in units of milliseconds. It's value is placed in the |
| * variable named, <code>timeMillisName</code>. |
| * |
| * @return The set of Statements containing the benchmark code along with the |
| * wrapped <code>stmts</code> |
| */ |
| private Statements benchmark(Statements stmts, String timeMillisName, |
| long bound, Statements recordCode, Statements breakCode) { |
| Statements benchmarkCode = new StatementsList(); |
| List<Statements> benchStatements = benchmarkCode.getStatements(); |
| |
| ForLoop loop = new ForLoop("int numLoops = 1", "true", ""); |
| benchStatements.add(loop); |
| List<Statements> loopStatements = loop.getStatements(); |
| |
| loopStatements.add(new Statement("long start = System.currentTimeMillis()")); |
| ForLoop runLoop = new ForLoop("int i = 0", "i < numLoops", "++i", stmts); |
| loopStatements.add(runLoop); |
| |
| // Put the rest of the code in 1 big statement to simplify things |
| String benchCode = "long duration = System.currentTimeMillis() - start;\n\n" |
| + |
| |
| "if ( duration < 150 ) {\n" |
| + " numLoops += numLoops;\n" |
| + " continue;\n" |
| + "}\n\n" |
| + |
| |
| "double durationMillis = duration * 1.0;\n" |
| + "double numLoopsAsDouble = numLoops * 1.0;\n" |
| + timeMillisName |
| + " = durationMillis / numLoopsAsDouble"; |
| |
| loopStatements.add(new Statement(benchCode)); |
| |
| if (recordCode != null) { |
| loopStatements.add(recordCode); |
| } |
| |
| if (bound != 0) { |
| loopStatements.add(new Statement("if ( numLoops == 1 && duration > " |
| + bound + " ) {\n" + breakCode.toString() + "\n" + "}\n\n")); |
| } |
| |
| loopStatements.add(new Statement("break")); |
| |
| return benchmarkCode; |
| } |
| |
| private boolean fieldExists(JClassType type, String fieldName) { |
| JField field = type.findField(fieldName); |
| if (field == null) { |
| JClassType superClass = type.getSuperclass(); |
| // noinspection SimplifiableIfStatement |
| if (superClass == null) { |
| return false; |
| } |
| return fieldExists(superClass, fieldName); |
| } |
| return true; |
| } |
| |
| private Statements genBenchTarget(JMethod beginMethod, JMethod endMethod, |
| List<String> paramNames, Statements test) { |
| Statements statements = new StatementsList(); |
| List<Statements> statementsList = statements.getStatements(); |
| |
| if (beginMethod != null) { |
| statementsList.add(new Statement(new MethodCall(beginMethod.getName(), |
| paramNames))); |
| } |
| |
| statementsList.add(test); |
| |
| if (endMethod != null) { |
| statementsList.add(new Statement( |
| new MethodCall(endMethod.getName(), null))); |
| } |
| |
| return statements; |
| } |
| |
| /** |
| * Currently, the benchmarking subsystem does not support async Benchmarks, so |
| * we need to generate some additional code that prevents the user from |
| * entering async mode in their Benchmark, even though we're using it |
| * internally. |
| * |
| * <p> |
| * Generates the code for the "supportsAsync" functionality in the |
| * translatable version of GWTTestCase. This includes: |
| * <ul> |
| * <li>the supportsAsync flag</li> |
| * <li>the supportsAsync method</li> |
| * <li>the privateDelayTestFinish method</li> |
| * <li>the privateFinishTest method</li> |
| * </ul> |
| * </p> |
| */ |
| private void generateAsyncCode() { |
| SourceWriter writer = getSourceWriter(); |
| |
| writer.println("private boolean supportsAsync;"); |
| writer.println(); |
| writer.println("public boolean supportsAsync() {"); |
| writer.println(" return supportsAsync;"); |
| writer.println("}"); |
| writer.println(); |
| writer.println("private void privateDelayTestFinish(int timeout) {"); |
| writer.println(" supportsAsync = true;"); |
| writer.println(" try {"); |
| writer.println(" delayTestFinish(timeout);"); |
| writer.println(" } finally {"); |
| writer.println(" supportsAsync = false;"); |
| writer.println(" }"); |
| writer.println("}"); |
| writer.println(); |
| writer.println("private void privateFinishTest() {"); |
| writer.println(" supportsAsync = true;"); |
| writer.println(" try {"); |
| writer.println(" finishTest();"); |
| writer.println(" } finally {"); |
| writer.println(" supportsAsync = false;"); |
| writer.println(" }"); |
| writer.println("}"); |
| writer.println(); |
| } |
| |
| /** |
| * Generates an empty JSNI function to help us benchmark function call |
| * overhead. |
| * |
| * We prevent our empty function call from being inlined by the compiler by |
| * making it a JSNI call. This works as of 1.3 RC 2, but smarter versions of |
| * the compiler may be able to inline JSNI. |
| * |
| * Things actually get pretty squirrelly in general when benchmarking function |
| * call overhead, because, depending upon the benchmark, the compiler may |
| * inline the benchmark into our benchmark loop, negating the cost we thought |
| * we were measuring. |
| * |
| * The best way to deal with this is for users to write micro-benchmarks such |
| * that the micro-benchmark does significantly more work than a function call. |
| * For example, if micro-benchmarking a function call, perform the function |
| * call 100K times within the micro-benchmark itself. |
| */ |
| private void generateEmptyFunc(SourceWriter writer) { |
| writer.println("private native void " + EMPTY_FUNC + "() /*-{"); |
| writer.println("}-*/;"); |
| writer.println(); |
| } |
| |
| private Map<String, String> getAnnotationMetaData(JMethod method, |
| MutableLong bound) throws UnableToCompleteException { |
| |
| IterationTimeLimit limit = method.getAnnotation(IterationTimeLimit.class); |
| // noinspection SimplifiableIfStatement |
| if (limit == null) { |
| bound.value = getDefaultTimeout(); |
| } else { |
| bound.value = limit.value(); |
| } |
| |
| Map<String, String> paramMetaData = new HashMap<String, String>(); |
| |
| JParameter[] params = method.getParameters(); |
| |
| for (JParameter param : params) { |
| RangeField rangeField = param.getAnnotation(RangeField.class); |
| if (rangeField != null) { |
| String fieldName = rangeField.value(); |
| JClassType enclosingType = method.getEnclosingType(); |
| if (!fieldExists(enclosingType, fieldName)) { |
| logger.log(TreeLogger.ERROR, "The RangeField annotation on " |
| + enclosingType + " at " + method + " specifies a field, " |
| + fieldName + ", which could not be found. Perhaps it is " |
| + "mis-spelled?", null); |
| throw new UnableToCompleteException(); |
| } |
| paramMetaData.put(param.getName(), fieldName); |
| continue; |
| } |
| RangeEnum rangeEnum = param.getAnnotation(RangeEnum.class); |
| if (rangeEnum != null) { |
| Class<? extends Enum<?>> enumClass = rangeEnum.value(); |
| // Handle inner classes |
| String className = enumClass.getName().replace('$', '.'); |
| paramMetaData.put(param.getName(), className + ".values()"); |
| continue; |
| } |
| |
| String msg = "The parameter, " + param.getName() + ", on method, " |
| + method.getName() + ", must have it's range specified" |
| + "by a RangeField or RangeEnum annotation."; |
| logger.log(TreeLogger.ERROR, msg, null); |
| throw new UnableToCompleteException(); |
| } |
| |
| return paramMetaData; |
| } |
| |
| private synchronized long getDefaultTimeout() |
| throws UnableToCompleteException { |
| if (defaultTimeout != -1) { |
| return defaultTimeout; |
| } |
| Method m = null; |
| try { |
| m = IterationTimeLimit.class.getDeclaredMethod("value"); |
| defaultTimeout = (Long) m.getDefaultValue(); |
| } catch (Exception e) { |
| /* |
| * Possibly one of: - NullPointerException (if somehow TimeLimit weren't |
| * an annotation or value() didn't have a default). - |
| * NoSuchMethodException if we somehow spelled value wrong - |
| * TypeNotPresentException if somehow value were some type of Class that |
| * couldn't be loaded instead of long It really doesn't make any |
| * difference, because regardless of what could possibly have failed, |
| * we'll still need to go this route. |
| */ |
| logger.log(TreeLogger.ERROR, |
| "Unable to retrieve the default benchmark time limit", e); |
| throw new UnableToCompleteException(); |
| } |
| |
| return defaultTimeout; |
| } |
| |
| private void implementParameterizedTestMethods() |
| throws UnableToCompleteException { |
| |
| Map<String, JMethod> parameterizedMethods = getParameterizedTestMethods( |
| getRequestedClass(), logger); |
| SourceWriter sw = getSourceWriter(); |
| JClassType type = getRequestedClass(); |
| |
| // For each test method, benchmark its: |
| // a) overhead (setup + teardown + loop + function calls) and |
| // b) execution time |
| // for all possible parameter values |
| for (Map.Entry<String, JMethod> entry : parameterizedMethods.entrySet()) { |
| String name = entry.getKey(); |
| JMethod method = entry.getValue(); |
| JMethod beginMethod = getBeginMethod(type, method); |
| JMethod endMethod = getEndMethod(type, method); |
| |
| sw.println("public void " + name + "() {"); |
| sw.indent(); |
| sw.println(" privateDelayTestFinish( 2000 );"); |
| sw.println(); |
| |
| MutableLong bound = new MutableLong(); |
| Map<String, String> metaDataByParams = getAnnotationMetaData(method, |
| bound); |
| validateParams(method, metaDataByParams); |
| |
| JParameter[] methodParams = method.getParameters(); |
| List<String> paramNames = new ArrayList<String>(methodParams.length); |
| for (int i = 0; i < methodParams.length; ++i) { |
| paramNames.add(methodParams[i].getName()); |
| } |
| |
| sw.print("final java.util.List<Iterable<?>> iterables = java.util.Arrays.asList( new Iterable<?>[] { "); |
| |
| for (int i = 0; i < paramNames.size(); ++i) { |
| String paramName = paramNames.get(i); |
| sw.print(ITERABLE_ADAPTER_CLASS + ".toIterable(" |
| + metaDataByParams.get(paramName) + ")"); |
| if (i != paramNames.size() - 1) { |
| sw.print(","); |
| } else { |
| sw.println("} );"); |
| } |
| sw.print(" "); |
| } |
| |
| sw.println("final " + PERMUTATION_ITERATOR_CLASS |
| + " permutationIt = new " + PERMUTATION_ITERATOR_CLASS |
| + "(iterables);\n" + DeferredCommand.class.getName() |
| + ".addCommand( new " + IncrementalCommand.class.getName() + "() {\n" |
| + " public boolean execute() {\n" |
| + " privateDelayTestFinish( 10000 );\n" |
| + " if ( permutationIt.hasNext() ) {\n" + " " |
| + PERMUTATION_ITERATOR_CLASS |
| + ".Permutation permutation = permutationIt.next();\n"); |
| |
| for (int i = 0; i < methodParams.length; ++i) { |
| JParameter methodParam = methodParams[i]; |
| String typeName = methodParam.getType().getQualifiedSourceName(); |
| String paramName = paramNames.get(i); |
| sw.println(" " + typeName + " " + paramName + " = (" + typeName |
| + ") permutation.getValues().get(" + i + ");"); |
| } |
| |
| final String setupTimingName = "__setupTiming"; |
| final String testTimingName = "__testTiming"; |
| |
| sw.println("double " + setupTimingName + " = 0;"); |
| sw.println("double " + testTimingName + " = 0;"); |
| |
| Statements setupBench = genBenchTarget(beginMethod, endMethod, |
| paramNames, new Statement(new MethodCall(EMPTY_FUNC, null))); |
| Statements testBench = genBenchTarget(beginMethod, endMethod, paramNames, |
| new Statement(new MethodCall(method.getName(), paramNames))); |
| |
| StringBuffer recordResultsCode = new StringBuffer(BENCHMARK_RESULTS_CLASS |
| + " results = __getOrCreateTestResult();\n" + TRIAL_CLASS |
| + " trial = new " + TRIAL_CLASS + "();\n" |
| + "trial.setRunTimeMillis( " + testTimingName + " - " |
| + setupTimingName + " );\n" |
| + "java.util.Map<String, String> variables = trial.getVariables();\n"); |
| |
| for (String paramName : paramNames) { |
| recordResultsCode.append("variables.put( \"").append(paramName).append( |
| "\", ").append(paramName).append(".toString() );\n"); |
| } |
| |
| recordResultsCode.append("results.getTrials().add( trial )"); |
| Statements recordCode = new Statement(recordResultsCode.toString()); |
| |
| Statements breakCode = new Statement(" permutationIt.skipCurrentRange()"); |
| setupBench = benchmark(setupBench, setupTimingName, 0, null, breakCode); |
| testBench = benchmark(testBench, testTimingName, bound.value, recordCode, |
| breakCode); |
| |
| Statements testAndSetup = new StatementsList(); |
| testAndSetup.getStatements().addAll(setupBench.getStatements()); |
| testAndSetup.getStatements().addAll(testBench.getStatements()); |
| |
| sw.println(testAndSetup.toString()); |
| |
| sw.println(" return true;\n" + " }\n" |
| + " privateFinishTest();\n" + " return false;\n" + " }\n" |
| + "} );\n"); |
| |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| |
| /** |
| * Overrides the zero-arg test methods that don't have any |
| * overloaded/parameterized versions. |
| * |
| * TODO(tobyr) This code shares a lot of similarity with |
| * implementParameterizedTestMethods and they should probably be refactored |
| * into a single function. |
| */ |
| private void implementZeroArgTestMethods() throws UnableToCompleteException { |
| Map<String, JMethod> zeroArgMethods = getNotOverloadedTestMethods(getRequestedClass()); |
| SourceWriter sw = getSourceWriter(); |
| JClassType type = getRequestedClass(); |
| |
| for (Map.Entry<String, JMethod> entry : zeroArgMethods.entrySet()) { |
| String name = entry.getKey(); |
| JMethod method = entry.getValue(); |
| JMethod beginMethod = getBeginMethod(type, method); |
| JMethod endMethod = getEndMethod(type, method); |
| |
| sw.println("public void " + name + "() {"); |
| sw.indent(); |
| |
| final String setupTimingName = "__setupTiming"; |
| final String testTimingName = "__testTiming"; |
| |
| sw.println("double " + setupTimingName + " = 0;"); |
| sw.println("double " + testTimingName + " = 0;"); |
| |
| Statements setupBench = genBenchTarget(beginMethod, endMethod, |
| Collections.<String> emptyList(), new Statement(new MethodCall( |
| EMPTY_FUNC, null))); |
| |
| StatementsList testStatements = new StatementsList(); |
| testStatements.getStatements().add( |
| new Statement(new MethodCall("super." + method.getName(), null))); |
| Statements testBench = genBenchTarget(beginMethod, endMethod, |
| Collections.<String> emptyList(), testStatements); |
| |
| String recordResultsCode = BENCHMARK_RESULTS_CLASS |
| + " results = __getOrCreateTestResult();\n" + TRIAL_CLASS |
| + " trial = new " + TRIAL_CLASS + "();\n" |
| + "trial.setRunTimeMillis( " + testTimingName + " - " |
| + setupTimingName + " );\n" + "results.getTrials().add( trial )"; |
| |
| Statements breakCode = new Statement(" break " + ESCAPE_LOOP); |
| |
| setupBench = benchmark(setupBench, setupTimingName, 0, null, breakCode); |
| testBench = benchmark(testBench, testTimingName, getDefaultTimeout(), |
| new Statement(recordResultsCode), breakCode); |
| ForLoop loop = (ForLoop) testBench.getStatements().get(0); |
| loop.setLabel(ESCAPE_LOOP); |
| |
| sw.println(setupBench.toString()); |
| sw.println(testBench.toString()); |
| |
| sw.outdent(); |
| sw.println("}"); |
| } |
| } |
| |
| private void validateParams(JMethod method, Map<String, String> params) |
| throws UnableToCompleteException { |
| JParameter[] methodParams = method.getParameters(); |
| for (JParameter methodParam : methodParams) { |
| String paramName = methodParam.getName(); |
| String paramValue = params.get(paramName); |
| |
| if (paramValue == null) { |
| String msg = "Could not find the meta data attribute " |
| + BENCHMARK_PARAM_META + " for the parameter " + paramName |
| + " on method " + method.getName(); |
| logger.log(TreeLogger.ERROR, msg, null); |
| throw new UnableToCompleteException(); |
| } |
| } |
| } |
| } |