A small patch to use the StringConcatFactory instead of String.format() to implement toString() of a data class
Remi Forax
forax at univ-mlv.fr
Sun Nov 12 21:58:09 UTC 2017
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