A small patch to use the StringConcatFactory instead of String.format() to implement toString() of a data class
Vicente Romero
vicente.romero at oracle.com
Mon Nov 13 17:58:50 UTC 2017
Hi Remi,
Thanks for the patch, seems useful. We could add it as an optional
implementation and make a comparison between the possible
implementations. Another possible implementation, again in the case of
the toString, is to generate an indy call directly from the data class
to a StringConcatFactory method,
Thanks,
Vicente
On 11/12/2017 04:58 PM, Remi Forax wrote:
> Hi all,
> A small patch to use the StringConcatFactory to implement the toString of a data class.
>
> The patch manage the case where there is no getter like the current code, even if you can not create a data class with no field using javac.
>
> cheers,
> Rémi
>
> diff -r a6a70e4541b3 src/java.base/share/classes/java/lang/invoke/ObjectMethodBuilders.java
> --- a/src/java.base/share/classes/java/lang/invoke/ObjectMethodBuilders.java Fri Nov 10 18:51:06 2017 +0100
> +++ b/src/java.base/share/classes/java/lang/invoke/ObjectMethodBuilders.java Sun Nov 12 22:54:42 2017 +0100
> @@ -189,49 +189,50 @@
>
> /**
> * Generates a method handle for the {@code toString} method for a given data class
> + * @param lookup the lookup object
> * @param receiverClass the data class
> * @param getters the list of getters
> * @param names the names
> * @return the method handle
> */
> - private static MethodHandle makeToString(Class<?> receiverClass,
> + private static MethodHandle makeToString(MethodHandles.Lookup lookup,
> + Class<?> receiverClass,
> List<MethodHandle> getters,
> List<String> names) {
> - // This is a pretty lousy algorithm; we spread the receiver over N places,
> - // apply the N getters, apply N toString operations, and concat the result with String.format
> - // Better to use String.format directly, or delegate to StringConcatFactory
> - // Also probably want some quoting around String components
> + // This is a simple algorithm; we spread the receiver over N places,
> + // apply the N getters, then delegate to StringConcatFactory
> + // We may want some quoting around String components ?
>
> assert getters.size() == names.size();
>
> - int[] invArgs = new int[getters.size()];
> - Arrays.fill(invArgs, 0);
> - MethodHandle[] filters = new MethodHandle[getters.size()];
> - StringBuilder sb = new StringBuilder();
> - sb.append(receiverClass.getSimpleName()).append("[");
> - for (int i=0; i<getters.size(); i++) {
> - MethodHandle getter = getters.get(i); // (R)T
> - MethodHandle stringify = stringifier(getter.type().returnType()); // (T)String
> - MethodHandle stringifyThisField = MethodHandles.filterArguments(stringify, 0, getter); // (R)String
> - filters[i] = stringifyThisField;
> - sb.append(names.get(i)).append("=%s");
> - if (i != getters.size() - 1)
> - sb.append(", ");
> - }
> - sb.append(']');
> - String formatString = sb.toString();
> - MethodHandle formatter = MethodHandles.insertArguments(STRING_FORMAT, 0, formatString)
> - .asCollector(String[].class, getters.size()); // (R*)String
> - if (getters.size() == 0) {
> - // Add back extra R
> - formatter = MethodHandles.dropArguments(formatter, 0, receiverClass);
> - }
> - else {
> - MethodHandle filtered = MethodHandles.filterArguments(formatter, 0, filters);
> - formatter = MethodHandles.permuteArguments(filtered, MethodType.methodType(String.class, receiverClass), invArgs);
> + if (getters.isEmpty()) {
> + return MethodHandles.dropArguments(
> + MethodHandles.constant(String.class, "[]"),
> + 0, receiverClass);
> }
>
> - return formatter;
> + Class<?>[] getterTypes = new Class<?>[getters.size()];
> + StringBuilder recipeBuilder = new StringBuilder();
> + recipeBuilder.append(receiverClass.getSimpleName()).append('[');
> + String separator = "";
> + for (int i = 0; i < getters.size(); i++) {
> + recipeBuilder.append(separator).append(names.get(i)).append("=\1");
> + getterTypes[i] = getters.get(i).type().returnType();
> + separator = ", ";
> + }
> + String recipe = recipeBuilder.append(']').toString();
> + MethodType concatType = MethodType.methodType(String.class, getterTypes);
> +
> + CallSite cs;
> + try {
> + cs = StringConcatFactory.makeConcatWithConstants(lookup, "toString", concatType, recipe);
> + } catch (StringConcatException e) {
> + throw (LinkageError)new LinkageError().initCause(e);
> + }
> + MethodHandle target = cs.dynamicInvoker();
> + target = MethodHandles.filterArguments(target, 0, getters.toArray(new MethodHandle[0]));
> + target = MethodHandles.permuteArguments(target, MethodType.methodType(String.class, receiverClass), new int[getters.size()]);
> + return target;
> }
>
> /**
> @@ -277,6 +278,6 @@
> */
> public static CallSite makeToString(MethodHandles.Lookup lookup, String invName, MethodType invType,
> Class<?> dataClass, String names, MethodHandle... getters) throws Throwable {
> - return new ConstantCallSite(makeToString(dataClass, List.of(getters), List.of(names.split(";"))));
> + return new ConstantCallSite(makeToString(lookup, dataClass, List.of(getters), List.of(names.split(";"))));
> }
> }
More information about the amber-dev
mailing list