8217047: Provide a way to inject missing parameter names
authorjlahoda
Thu, 11 Apr 2019 17:55:18 +0200
changeset 54514 b0651dcc8d98
parent 54513 2fd0422ac495
child 54515 138f47e9d8c4
8217047: Provide a way to inject missing parameter names Summary: Adding a way to provide parameter names that are missing in the classfiles. Reviewed-by: darcy, jjg
src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java
src/jdk.compiler/share/classes/com/sun/source/util/ParameterNameProvider.java
src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/MissingInfoHandler.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
test/langtools/tools/javac/api/lazy/LoadParameterNamesLazily.java
--- a/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java	Thu Apr 11 15:36:09 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java	Thu Apr 11 17:55:18 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -29,6 +29,7 @@
 
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.lang.model.element.Element;
+import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Elements;
 import javax.lang.model.util.Types;
@@ -137,6 +138,27 @@
     public abstract void removeTaskListener(TaskListener taskListener);
 
     /**
+     * Sets the specified {@link ParameterNameProvider}. It may be used when
+     * {@link VariableElement#getSimpleName()} is called for a method parameter
+     * for which an authoritative name is not found. The given
+     * {@code ParameterNameProvider} may infer a user-friendly name
+     * for the method parameter.
+     *
+     * Setting a new {@code ParameterNameProvider} will clear any previously set
+     * {@code ParameterNameProvider}, which won't be queried any more.
+     *
+     * When no {@code ParameterNameProvider} is set, or when it returns null from
+     * {@link ParameterNameProvider#getParameterName(javax.lang.model.element.VariableElement)},
+     * an automatically synthesized name is returned from {@code VariableElement.getSimpleName()}.
+     *
+     * @implSpec The default implementation of this method does nothing.
+     *
+     * @param provider the provider.
+     * @since 13
+     */
+    public void setParameterNameProvider(ParameterNameProvider provider) {}
+
+    /**
      * Returns a type mirror of the tree node determined by the specified path.
      * This method has been superceded by methods on
      * {@link com.sun.source.util.Trees Trees}.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/ParameterNameProvider.java	Thu Apr 11 17:55:18 2019 +0200
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 com.sun.source.util;
+
+import javax.lang.model.element.VariableElement;
+
+/**
+ * A provider for parameter names when the parameter names are not determined from
+ * a reliable source, like a classfile.
+ *
+ * @since 13
+ */
+public interface ParameterNameProvider {
+
+    /**
+     * Infer a parameter name for the given parameter. The implementations of this method
+     * should infer parameter names in such a way that the parameter names are distinct
+     * for any given owning method.
+     *
+     * If the implementation of this method returns null, an automatically synthesized name is used.
+     *
+     * @param parameter the parameter for which the name should be inferred.
+     * @return a user-friendly name for the parameter, or null if unknown
+     */
+    public CharSequence getParameterName(VariableElement parameter);
+
+}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java	Thu Apr 11 15:36:09 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java	Thu Apr 11 17:55:18 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -43,9 +43,11 @@
 import com.sun.source.tree.CompilationUnitTree;
 import com.sun.source.tree.Tree;
 import com.sun.source.util.JavacTask;
+import com.sun.source.util.ParameterNameProvider;
 import com.sun.source.util.Plugin;
 import com.sun.source.util.TaskListener;
 import com.sun.tools.doclint.DocLint;
+import com.sun.tools.javac.code.MissingInfoHandler;
 import com.sun.tools.javac.main.JavaCompiler;
 import com.sun.tools.javac.model.JavacElements;
 import com.sun.tools.javac.model.JavacTypes;
@@ -123,6 +125,11 @@
         mtl.remove(taskListener);
     }
 
+    @Override
+    public void setParameterNameProvider(ParameterNameProvider handler) {
+        MissingInfoHandler.instance(context).setDelegate(handler);
+    }
+
     public Collection<TaskListener> getTaskListeners() {
         MultiTaskListener mtl = MultiTaskListener.instance(context);
         return mtl.getTaskListeners();
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java	Thu Apr 11 15:36:09 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java	Thu Apr 11 17:55:18 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -319,6 +319,11 @@
      */
     public static final long BODY_ONLY_FINALIZE = 1L<<17; //blocks only
 
+    /**
+     * Flag to indicate the given ParamSymbol has a user-friendly name filled.
+     */
+    public static final long NAME_FILLED = 1L<<58; //ParamSymbols only
+
     /** Modifier masks.
      */
     public static final int
@@ -435,7 +440,8 @@
         DEPRECATED_REMOVAL(Flags.DEPRECATED_REMOVAL),
         HAS_RESOURCE(Flags.HAS_RESOURCE),
         POTENTIALLY_AMBIGUOUS(Flags.POTENTIALLY_AMBIGUOUS),
-        ANONCONSTR_BASED(Flags.ANONCONSTR_BASED);
+        ANONCONSTR_BASED(Flags.ANONCONSTR_BASED),
+        NAME_FILLED(Flags.NAME_FILLED);
 
         Flag(long flag) {
             this.value = flag;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/MissingInfoHandler.java	Thu Apr 11 17:55:18 2019 +0200
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 com.sun.tools.javac.code;
+
+import com.sun.source.util.ParameterNameProvider;
+import com.sun.tools.javac.code.Symbol.ParamSymbol;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.Name;
+import com.sun.tools.javac.util.Names;
+
+/**
+ * A Context class, that can return additional useful information for Symbols, currently
+ * parameter names. It does so by calling user-supplied {@link ParameterNameProvider}.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+public class MissingInfoHandler {
+    protected static final Context.Key<MissingInfoHandler> missingInfoHandlerWrapperKey = new Context.Key<>();
+
+    public static MissingInfoHandler instance(Context context) {
+        MissingInfoHandler instance = context.get(missingInfoHandlerWrapperKey);
+        if (instance == null)
+            instance = new MissingInfoHandler(context);
+        return instance;
+    }
+
+    private final Names names;
+    private ParameterNameProvider parameterNameProvider;
+
+    protected MissingInfoHandler(Context context) {
+        context.put(missingInfoHandlerWrapperKey, this);
+        names = Names.instance(context);
+    }
+
+    public Name getParameterName(ParamSymbol parameter) {
+        if (parameterNameProvider != null) {
+            CharSequence name = parameterNameProvider.getParameterName(parameter);
+            if (name != null) {
+                return names.fromString(name.toString());
+            }
+        }
+
+        return null;
+    }
+
+    public void setDelegate(ParameterNameProvider delegate) {
+        this.parameterNameProvider = delegate;
+    }
+}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Apr 11 15:36:09 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Apr 11 17:55:18 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -65,9 +65,12 @@
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Kinds.*;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
+import com.sun.tools.javac.code.MissingInfoHandler;
 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
 import com.sun.tools.javac.code.Scope.WriteableScope;
+import com.sun.tools.javac.code.Symbol;
 import static com.sun.tools.javac.code.Symbol.OperatorSymbol.AccessCode.FIRSTASGOP;
+import com.sun.tools.javac.code.Type;
 import static com.sun.tools.javac.code.TypeTag.CLASS;
 import static com.sun.tools.javac.code.TypeTag.FORALL;
 import static com.sun.tools.javac.code.TypeTag.TYPEVAR;
@@ -76,6 +79,7 @@
 import static com.sun.tools.javac.jvm.ByteCodes.lushrl;
 import static com.sun.tools.javac.jvm.ByteCodes.lxor;
 import static com.sun.tools.javac.jvm.ByteCodes.string_add;
+import com.sun.tools.javac.util.Name;
 
 /** Root class for Java symbols. It contains subclasses
  *  for specific sorts of symbols, such as variables, methods and operators,
@@ -1189,6 +1193,16 @@
 
     }
 
+    public static class RootPackageSymbol extends PackageSymbol {
+        public final MissingInfoHandler missingInfoHandler;
+
+        public RootPackageSymbol(Name name, Symbol owner, MissingInfoHandler missingInfoHandler) {
+            super(name, owner);
+            this.missingInfoHandler = missingInfoHandler;
+        }
+
+    }
+
     /** A class for class symbols
      */
     public static class ClassSymbol extends TypeSymbol implements TypeElement {
@@ -1634,6 +1648,32 @@
         }
     }
 
+    public static class ParamSymbol extends VarSymbol {
+        public ParamSymbol(long flags, Name name, Type type, Symbol owner) {
+            super(flags, name, type, owner);
+        }
+
+        @Override
+        public Name getSimpleName() {
+            if ((flags_field & NAME_FILLED) == 0) {
+                flags_field |= NAME_FILLED;
+                Symbol rootPack = this;
+                while (rootPack != null && !(rootPack instanceof RootPackageSymbol)) {
+                    rootPack = rootPack.owner;
+                }
+                if (rootPack != null) {
+                    Name inferredName =
+                            ((RootPackageSymbol) rootPack).missingInfoHandler.getParameterName(this);
+                    if (inferredName != null) {
+                        this.name = inferredName;
+                    }
+                }
+            }
+            return super.getSimpleName();
+        }
+
+    }
+
     /** A class for method symbols.
      */
     public static class MethodSymbol extends Symbol implements ExecutableElement {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Thu Apr 11 15:36:09 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Thu Apr 11 17:55:18 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -42,6 +42,7 @@
 import com.sun.tools.javac.code.Symbol.MethodSymbol;
 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
 import com.sun.tools.javac.code.Symbol.PackageSymbol;
+import com.sun.tools.javac.code.Symbol.RootPackageSymbol;
 import com.sun.tools.javac.code.Symbol.TypeSymbol;
 import com.sun.tools.javac.code.Symbol.VarSymbol;
 import com.sun.tools.javac.code.Type.BottomType;
@@ -382,7 +383,9 @@
 
         messages = JavacMessages.instance(context);
 
-        rootPackage = new PackageSymbol(names.empty, null);
+        MissingInfoHandler missingInfoHandler = MissingInfoHandler.instance(context);
+
+        rootPackage = new RootPackageSymbol(names.empty, null, missingInfoHandler);
 
         // create the basic builtin symbols
         unnamedModule = new ModuleSymbol(names.empty, null) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Thu Apr 11 15:36:09 2019 +0100
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Thu Apr 11 17:55:18 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2019, 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
@@ -2557,14 +2557,12 @@
                 firstParam += skip;
             }
         }
-        List<Name> paramNames = List.nil();
+        Set<Name> paramNames = new HashSet<>();
         ListBuffer<VarSymbol> params = new ListBuffer<>();
         int nameIndex = firstParam;
         int annotationIndex = 0;
         for (Type t: sym.type.getParameterTypes()) {
-            Name name = parameterName(nameIndex, paramNames);
-            paramNames = paramNames.prepend(name);
-            VarSymbol param = new VarSymbol(PARAMETER, name, t, sym);
+            VarSymbol param = parameter(nameIndex, t, sym, paramNames);
             params.append(param);
             if (parameterAnnotations != null) {
                 ParameterAnnotations annotations = parameterAnnotations[annotationIndex];
@@ -2589,18 +2587,24 @@
     // Returns the name for the parameter at position 'index', either using
     // names read from the MethodParameters, or by synthesizing a name that
     // is not on the 'exclude' list.
-    private Name parameterName(int index, List<Name> exclude) {
+    private VarSymbol parameter(int index, Type t, MethodSymbol owner, Set<Name> exclude) {
+        long flags = PARAMETER;
+        Name argName;
         if (parameterNameIndices != null && index < parameterNameIndices.length
                 && parameterNameIndices[index] != 0) {
-            return readName(parameterNameIndices[index]);
+            argName = readName(parameterNameIndices[index]);
+            flags |= NAME_FILLED;
+        } else {
+            String prefix = "arg";
+            while (true) {
+                argName = names.fromString(prefix + exclude.size());
+                if (!exclude.contains(argName))
+                    break;
+                prefix += "$";
+            }
         }
-        String prefix = "arg";
-        while (true) {
-            Name argName = names.fromString(prefix + exclude.size());
-            if (!exclude.contains(argName))
-                return argName;
-            prefix += "$";
-        }
+        exclude.add(argName);
+        return new ParamSymbol(flags, argName, t, owner);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/api/lazy/LoadParameterNamesLazily.java	Thu Apr 11 17:55:18 2019 +0200
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+/*
+ * @test
+ * @bug 8217047
+ * @summary Verify the parameter names can be injected using ParameterNameProvider.
+ * @library /tools/lib
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ * @build toolbox.JavacTask toolbox.TestRunner toolbox.ToolBox
+ * @run main LoadParameterNamesLazily
+ */
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+
+import com.sun.source.util.*;
+
+import toolbox.JavacTask;
+import toolbox.Task;
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+
+public class LoadParameterNamesLazily extends TestRunner {
+
+    public static void main(String... args) throws Exception {
+        LoadParameterNamesLazily t = new LoadParameterNamesLazily();
+        t.runTests();
+    }
+
+    private static final String libClass =
+            "package lib;" +
+            "/**Lib javadoc.*/" +
+            "public class Lib {" +
+            "    /**Lib method javadoc.*/" +
+            "    public static void m(int param, int other) {}" +
+            "}";
+    private final ToolBox tb = new ToolBox();
+
+    LoadParameterNamesLazily() throws IOException {
+        super(System.err);
+    }
+
+    @Test
+    public void testLoadTreesLazily() throws IOException {
+        Path libSrc = Paths.get("lib-src");
+        tb.writeJavaFiles(libSrc, libClass);
+        Path libClasses = Paths.get("lib-classes");
+        Files.createDirectories(libClasses);
+
+        new JavacTask(tb)
+              .outdir(libClasses)
+              .options()
+              .files(tb.findJavaFiles(libSrc))
+              .run(Task.Expect.SUCCESS)
+              .writeAll();
+
+        Path src = Paths.get("src");
+        tb.writeJavaFiles(src,
+                          "class Use {" +
+                          " lib.Lib lib;" +
+                          "}");
+        Path classes = Paths.get("classes");
+        Files.createDirectories(classes);
+
+        new JavacTask(tb)
+              .outdir(classes)
+              .options("-classpath", libClasses.toString())
+              .files(tb.findJavaFiles(src))
+              .callback(task -> {
+                  task.setParameterNameProvider(parameter -> {
+                      ExecutableElement method = (ExecutableElement) parameter.getEnclosingElement();
+                      TypeElement clazz =
+                              (TypeElement) method.getEnclosingElement();
+                      if (clazz.getQualifiedName().contentEquals("lib.Lib")) {
+                          if (method.getParameters().indexOf(parameter) == 0) {
+                              return "testName";
+                          } else {
+                              return null;
+                          }
+                      }
+                      return null;
+                  });
+                  task.addTaskListener(new TaskListener() {
+                      @Override
+                      public void finished(TaskEvent e) {
+                          if (e.getKind() == TaskEvent.Kind.ANALYZE) {
+                              TypeElement lib = task.getElements().getTypeElement("lib.Lib");
+                              lib.getClass(); //not null
+                              ExecutableElement method =
+                                      ElementFilter.methodsIn(lib.getEnclosedElements()).get(0);
+                              Name paramName0 = method.getParameters().get(0).getSimpleName();
+                              if (!paramName0.contentEquals("testName")) {
+                                  throw new IllegalStateException("Unexpected parameter name: " +
+                                                                  paramName0);
+                              }
+                              Name paramName1 = method.getParameters().get(1).getSimpleName();
+                              if (!paramName1.contentEquals("arg1")) {
+                                  throw new IllegalStateException("Unexpected parameter name: " +
+                                                                  paramName1);
+                              }
+                          }
+                      }
+                  });
+              })
+              .run(Task.Expect.SUCCESS)
+              .writeAll();
+    }
+
+}