Skip to content

Commit

Permalink
Simplify StringTemplate matadata
Browse files Browse the repository at this point in the history
  • Loading branch information
JimLaskey committed Mar 11, 2024
1 parent 5d8510b commit fe0100c
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 40 deletions.
22 changes: 3 additions & 19 deletions src/java.base/share/classes/java/lang/String.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import jdk.internal.access.JavaTemplateAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.template.StringTemplateMetaData;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
Expand Down Expand Up @@ -4528,27 +4529,10 @@ public static String format(StringTemplate st) {
*/
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES)
public static String format(Locale l, StringTemplate st) {
JavaTemplateAccess JTA = SharedSecrets.getJavaTemplateAccess();
if (JTA.isLiteral(st)) {
StringFormatMetaData metaData = JTA.getMetaData(st, STRING_FORMAT_OWNER, () -> {
MethodHandle mh = FormatterBuilder.create(st, l);
return new StringFormatMetaData(l, mh);
});
if (metaData.l == l) {
try {
return (String)metaData.formatter().invokeExact(st);
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
}
}

return new Formatter(l).format(st).toString();
String result = FormatterBuilder.format(l, st);
return result != null ? result : new Formatter(l).format(st).toString();
}

private record StringFormatMetaData(Locale l, MethodHandle formatter) {}
private static final Object STRING_FORMAT_OWNER = new Object();

/**
* Formats using this string as the format string, and the supplied
* arguments.
Expand Down
29 changes: 8 additions & 21 deletions src/java.base/share/classes/java/util/Formatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2842,10 +2842,6 @@ public Formatter format(StringTemplate st) {
return format(l, st);
}

private record FormatterMetaData(Locale l, MethodHandle formatter) {}
private static final Object FORMATTER_OWNER = new Object();
JavaTemplateAccess JTA = SharedSecrets.getJavaTemplateAccess();

/**
* Writes a formatted string to this object's destination using the
* specified format and values embedded in (@code st}. Locale is specified
Expand Down Expand Up @@ -2880,25 +2876,16 @@ private record FormatterMetaData(Locale l, MethodHandle formatter) {}
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES)
public Formatter format(Locale l, StringTemplate st) {
ensureOpen();

if (JTA.isLiteral(st)) {
FormatterMetaData metaData = JTA.getMetaData(st, FORMATTER_OWNER, () -> {
MethodHandle mh = FormatterBuilder.create(st, l);
return new FormatterMetaData(l, mh);
});

if (metaData.l == l) {
try {
String result = (String)metaData.formatter().invokeExact(st);
a.append(result);
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
return this;
String result = FormatterBuilder.format(l, st);
if (result != null) {
try {
a.append(result);
} catch (IOException ioe) {
lastException = ioe;
}
return this;
}

return format(l, stringTemplateFormat(st.fragments()), st.values().toArray(new Object[0]));
return format(l, stringTemplateFormat(st.fragments()), st.values().toArray(Object[]::new));
}

/**
Expand Down
44 changes: 44 additions & 0 deletions src/java.base/share/classes/java/util/FormatterBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import jdk.internal.access.JavaTemplateAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.template.StringTemplateMetaData;
import jdk.internal.util.FormatConcatItem;

import sun.util.locale.provider.LocaleProviderAdapter;
Expand Down Expand Up @@ -604,4 +605,47 @@ private MethodHandle build() {
throw new AssertionError("concat fail", ex);
}
}

/**
* Format the {@link StringTemplate} from a cached {@link MethodHandle} if possible. Return null otherwise
* so that an unoptimized method can be used.
*
* @param l Locale
* @param st associated {@link StringTemplate}
* @return the formatted string or null if no cached {@link MethodHandle}
*/
public static String format(Locale l, StringTemplate st) {
FormatterBuilderMetaData metaData = FormatterBuilderMetaData.getMetaData(st, FormatterBuilderMetaData.class,
() -> new FormatterBuilderMetaData(st, l));
if (metaData != null && metaData.l == l) {
return metaData.invoke(st);
}
return null;
}

/**
* Metadata for {@link FormatterBuilder}.
*/
private static class FormatterBuilderMetaData extends StringTemplateMetaData {
/**
* Locale use for {@link MethodHandle}.
*/
private final Locale l;

/**
* Constructor.
* @param st associated {@link StringTemplate}
* @param l Locale
*/
FormatterBuilderMetaData(StringTemplate st, Locale l) {
super(st);
this.l = l;
}

@Override
public MethodHandle createMethodHandle(StringTemplate st) {
return FormatterBuilder.create(st, l);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (c) 2023, 2024, 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 jdk.internal.template;

import jdk.internal.access.JavaTemplateAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.Stable;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.StringConcatException;
import java.lang.invoke.StringConcatFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/**
* This class provides a model for associating metadata by an owning class with a literal
* StringTemplate.
*/
public class StringTemplateMetaData {
private static final JavaTemplateAccess JTA = SharedSecrets.getJavaTemplateAccess();

/**
* Per {@link StringTemplate} {@link MethodHandle}.
*/
@Stable
private MethodHandle methodHandle;

/**
* Public constructor.
*
* @param st associated {@link StringTemplate}
*/
public StringTemplateMetaData(StringTemplate st) {
this.methodHandle = null;
}

/**
* {@return the method handle}
*/
public MethodHandle methodHandle() {
return methodHandle;
}

/**
* Set the method handle.
*
* @param methodHandle the method handle to set
*/
public void setMethodHandle(MethodHandle methodHandle) {
this.methodHandle = methodHandle;
}

/**
* Construct the {@link MethodHandle}. Override if special handling is required.
* @param st associated {@link StringTemplate}
* @return
*/
public MethodHandle createMethodHandle(StringTemplate st) {
List<String> fragments = filterFragments(st.fragments());
List<Class<?>> ptypes = JTA.getTypes(st);
MethodHandle[] filters = createValueFilters(ptypes);
List<Class<?>> fTypes = new ArrayList<>();
for (int i = 0; i < filters.length; i++) {
if (filters[i] == null) {
fTypes.add(ptypes.get(i));
} else {
fTypes.add(filters[i].type().returnType());
}
}
MethodHandle mh = createConcat(fragments, fTypes);
mh = JTA.bindTo(st, mh);
return mh;
}

/**
* Filter the associated {@link StringTemplate StringTemplate's} fragments. Override
* if special handling is required.
*
* @param fragments associated {@link StringTemplate StringTemplate's} fragments
*
* @return filtered fragments
*/
public List<String> filterFragments(List<String> fragments) {
return fragments;
}

/**
* Create value filters for associated {@link StringTemplate StringTemplate's} values.
* Override if special handling is required.
*
* @param ptypes value types
*
* @return array of {@link MethodHandle} containing filters for each value or null if
* a value needs no filtering.
*/
public MethodHandle[] createValueFilters(List<Class<?>> ptypes) {
return new MethodHandle[ptypes.size()];
}

/**
* Create the concat {@link MethodHandle}.
*
* @param fragments concat fragments
* @param ptypes concat value types
*
* @return {@link MethodHandle} that performs the basic concat.
*/
public MethodHandle createConcat(List<String> fragments, List<Class<?>> ptypes) {
MethodHandle mh;
try {
mh = StringConcatFactory.makeConcatWithTemplate(fragments, ptypes);
} catch (StringConcatException e) {
throw new InternalError(e);
}
return mh;
}

public String invoke(StringTemplate st) {
if (methodHandle != null) {
try {
return (String)methodHandle.invokeExact(st);
} catch (Throwable e) {
throw new InternalError(e);
}
}
return null;
}

/**
* Method to set up the metadata. Returns null if the {@link StringTemplate} has a
* different owner.
*
* @param st associated {@link StringTemplate}
* @param owner metadata owner
* @param supplier supplier to generate instance of metadata
*
* @return instance of the metadata class.
*/
public static <M extends StringTemplateMetaData> M getMetaData(StringTemplate st, Object owner, Supplier<M> supplier) {
if (JTA.isLiteral(st)) {
M metaData = JTA.getMetaData(st, owner, () -> {
M md = supplier.get();
md.setMethodHandle(md.createMethodHandle(st));
return md;
});
return metaData;
}
return null;
}
}

0 comments on commit fe0100c

Please sign in to comment.