# HG changeset patch # User jlahoda # Date 1548073426 -3600 # Mon Jan 21 13:23:46 2019 +0100 # Node ID 45f13597581284fcf73dd9e1b3e90cdcb37cf954 # Parent 2e1fd6414c4bbdf9953336480a9a5dc048486fb5 imported patch 8217047 diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java b/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java --- a/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java +++ b/src/jdk.compiler/share/classes/com/sun/source/util/JavacTask.java @@ -137,6 +137,21 @@ public abstract void removeTaskListener(TaskListener taskListener); /** + * Sets the specified {@linkplain ParameterNameProvider}. It may be used to when + * {@link VariablElement#getSimpleName} is called for a method parameter + * for which an authoritative name is not found. The given + * {@linkplain ParameterNameProvider} may infer a user-friendly name + * for the method parameter. + * + * Setting a new {@linkplain ParameterNameProvider} will clear any previously set + * {@linkplain ParameterNameProvider}, which won't be queried any more. + * + * @param handler the handler. + * @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}. diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/ParameterNameProvider.java b/src/jdk.compiler/share/classes/com/sun/source/util/ParameterNameProvider.java new file mode 100644 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/source/util/ParameterNameProvider.java @@ -0,0 +1,48 @@ +/* + * 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; + +/** + * An optional provider for parameter names when the parameter names are not determined from + * a reliable source, like classfile. + * + * @since 13 + */ +public interface ParameterNameProvider { + + /** + * Infer 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. + * + * @param parameter the parameter for which the name should be inferred. + * @return a user-friendly name for the parameter + */ + public CharSequence getParameterName(VariableElement parameter); + +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java @@ -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 getTaskListeners() { MultiTaskListener mtl = MultiTaskListener.instance(context); return mtl.getTaskListeners(); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java @@ -319,6 +319,11 @@ */ public static final long BODY_ONLY_FINALIZE = 1L<<17; //blocks only + /** + * Flag to indicate the given ParamSymbol has a user-friedly 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; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/MissingInfoHandler.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/MissingInfoHandler.java new file mode 100644 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/MissingInfoHandler.java @@ -0,0 +1,73 @@ +/* + * 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; + +/** + * + *

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. + */ +public class MissingInfoHandler { + protected static final Context.Key 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; + } +} diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -64,9 +64,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; @@ -75,6 +78,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, @@ -1188,6 +1192,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 { @@ -1633,6 +1647,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 { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java @@ -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) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -144,6 +144,8 @@ DeferredCompletionFailureHandler dcfh; + MissingInfoHandler missingInfoHandler; + /** * Support for preview language features. */ @@ -264,6 +266,7 @@ throw new AssertionError("FileManager initialization error"); diagFactory = JCDiagnostic.Factory.instance(context); dcfh = DeferredCompletionFailureHandler.instance(context); + missingInfoHandler = MissingInfoHandler.instance(context); log = Log.instance(context); @@ -2553,14 +2556,12 @@ firstParam += skip; } } - List paramNames = List.nil(); + Set paramNames = new HashSet<>(); ListBuffer 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]; @@ -2585,18 +2586,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 exclude) { + private VarSymbol parameter(int index, Type t, MethodSymbol owner, Set 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); } /** diff --git a/test/langtools/tools/javac/api/lazy/LoadParameterNamesLazily.java b/test/langtools/tools/javac/api/lazy/LoadParameterNamesLazily.java new file mode 100644 --- /dev/null +++ b/test/langtools/tools/javac/api/lazy/LoadParameterNamesLazily.java @@ -0,0 +1,128 @@ +/* + * 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) {}" + + "}"; + 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 -> { + TypeElement clazz = + (TypeElement) parameter.getEnclosingElement().getEnclosingElement(); + if (clazz.getQualifiedName().contentEquals("lib.Lib")) { + return "testName"; + } + 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 paramName = method.getParameters().get(0).getSimpleName(); + if (!paramName.contentEquals("testName")) { + throw new IllegalStateException("Unexpected parameter name: " + + paramName); + } + } + } + }); + }) + .run(Task.Expect.SUCCESS) + .writeAll(); + } + +}