/* * Copyright (c) 2023, 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 java.lang.runtime; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.StringConcatException; import java.lang.invoke.StringConcatFactory; import java.util.Arrays; import java.util.List; /** * This class synthesizes {@link StringTemplate StringTemplates} based on * fragments and bootstrap method type. Usage is primarily from * {@link java.lang.runtime.TemplateRuntime}. * * @since 21 * * Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES. * Do not rely on its availability. */ final class StringTemplateImplFactory { /** * Private constructor. */ StringTemplateImplFactory() { throw new AssertionError("private constructor"); } /* * {@link StringTemplateImpl} constructor MethodHandle. */ private static final MethodHandle CONSTRUCTOR; /* * Frequently used method types. */ private static final MethodType MT_STRING_STIMPL = MethodType.methodType(String.class, StringTemplateImpl.class); private static final MethodType MT_LIST_STIMPL = MethodType.methodType(List.class, StringTemplateImpl.class); /** * List (for nullable) of MethodHandle; */ private static final MethodHandle TO_LIST; static { try { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(void.class, int.class, int.class, List.class, MethodHandle.class, MethodHandle.class); CONSTRUCTOR = lookup.findConstructor(StringTemplateImpl.class, mt) .asType(mt.changeReturnType(Carriers.CarrierObject.class)); mt = MethodType.methodType(List.class, Object[].class); TO_LIST = lookup.findStatic(StringTemplateImplFactory.class, "toList", mt); } catch(ReflectiveOperationException ex) { throw new AssertionError("carrier static init fail", ex); } } /** * Create a new {@link StringTemplateImpl} constructor. * * @param fragments string template fragments * @param type values types with StringTemplate return * * @return {@link MethodHandle} that can construct a {@link StringTemplateImpl} with arguments * used as values. */ static MethodHandle createStringTemplateImplMH(List fragments, MethodType type) { Carriers.CarrierElements elements = Carriers.CarrierFactory.of(type); MethodHandle[] components = elements .components() .stream() .map(c -> c.asType(c.type().changeParameterType(0, StringTemplateImpl.class))) .toArray(MethodHandle[]::new); Class[] ptypes = elements .components() .stream() .map(c -> c.type().returnType()) .toArray(Class[]::new); int[] permute = new int[ptypes.length]; MethodHandle interpolateMH; MethodType mt; try { interpolateMH = StringConcatFactory.makeConcatWithTemplate(fragments, List.of(ptypes)); } catch (StringConcatException ex) { throw new RuntimeException("constructing internal string template", ex); } interpolateMH = MethodHandles.filterArguments(interpolateMH, 0, components); interpolateMH = MethodHandles.permuteArguments(interpolateMH, MT_STRING_STIMPL, permute); mt = MethodType.methodType(List.class, ptypes); MethodHandle valuesMH = TO_LIST.asCollector(Object[].class, components.length).asType(mt); valuesMH = MethodHandles.filterArguments(valuesMH, 0, components); valuesMH = MethodHandles.permuteArguments(valuesMH, MT_LIST_STIMPL, permute); MethodHandle constructor = MethodHandles.insertArguments(CONSTRUCTOR, 0, elements.primitiveCount(), elements.objectCount(), fragments, valuesMH, interpolateMH); constructor = MethodHandles.foldArguments(elements.initializer(), 0, constructor); mt = MethodType.methodType(StringTemplate.class, ptypes); constructor = constructor.asType(mt); return constructor; } /** * Generic {@link StringTemplate}. * * @param fragments immutable list of string fragments from string template * @param values immutable list of expression values */ private record SimpleStringTemplate(List fragments, List values) implements StringTemplate { @Override public String toString() { return StringTemplate.toString(this); } } /** * Returns a new StringTemplate composed from fragments and values. * * @param fragments array of string fragments * @param values array of expression values * * @return StringTemplate composed from fragments and values */ static StringTemplate newTrustedStringTemplate(String[] fragments, Object[] values) { return new SimpleStringTemplate(List.of(fragments), toList(values)); } /** * Returns a new StringTemplate composed from fragments and values. * * @param fragments list of string fragments * @param values array of expression values * * @return StringTemplate composed from fragments and values */ static StringTemplate newTrustedStringTemplate(List fragments, Object[] values) { return new SimpleStringTemplate(List.copyOf(fragments), toList(values)); } /** * Returns a new StringTemplate composed from fragments and values. * * @param fragments list of string fragments * @param values list of expression values * * @return StringTemplate composed from fragments and values */ static StringTemplate newStringTemplate(List fragments, List values) { @SuppressWarnings("unchecked") List copy = (List)values.stream().toList(); return new SimpleStringTemplate(List.copyOf(fragments), copy); } /** * Collect nullable elements from an array into a unmodifiable list. * Elements are guaranteed to be safe. * * @param elements elements to place in list * * @return unmodifiable list. */ private static List toList(Object[] elements) { return Arrays.stream(elements).toList(); } }