diff a/src/jdk.incubator.code/share/classes/jdk/incubator/code/bytecode/impl/LambdaExpansionTransformer.java b/src/jdk.incubator.code/share/classes/jdk/incubator/code/bytecode/impl/LambdaExpansionTransformer.java --- /dev/null +++ b/src/jdk.incubator.code/share/classes/jdk/incubator/code/bytecode/impl/LambdaExpansionTransformer.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2026, 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.incubator.code.bytecode.impl; + +import java.lang.constant.ClassDesc; +import java.lang.constant.DirectMethodHandleDesc; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.SequencedMap; +import java.util.Set; +import java.util.stream.Stream; + +import jdk.incubator.code.Block; +import jdk.incubator.code.CodeTransformer; +import jdk.incubator.code.CodeType; +import jdk.incubator.code.Op; +import jdk.incubator.code.Quoted; +import jdk.incubator.code.Value; +import jdk.incubator.code.dialect.core.CoreOp; +import jdk.incubator.code.dialect.core.CoreOp.FuncOp; +import jdk.incubator.code.dialect.core.CoreType; +import jdk.incubator.code.dialect.core.FunctionType; +import jdk.incubator.code.dialect.core.VarType; +import jdk.incubator.code.dialect.java.FieldRef; +import jdk.incubator.code.dialect.java.JavaOp; +import jdk.incubator.code.dialect.java.JavaType; +import jdk.incubator.code.dialect.java.MethodRef; +import jdk.incubator.code.extern.DialectFactory; +import jdk.incubator.code.internal.OpBuilder; +import jdk.incubator.code.runtime.ReflectableLambdaMetafactory; + +import static java.lang.constant.ConstantDescs.*; + +/** + * Lambda expansion transformer generates a module with lambda operations replaced + * by dynamic function calls and with synthetic functions for lambda bodies and + * reflectable lambda model builders. + */ +final class LambdaExpansionTransformer implements CodeTransformer { + + private static final DirectMethodHandleDesc DMHD_LAMBDA_METAFACTORY = ofCallsiteBootstrap( + LambdaMetafactory.class.describeConstable().orElseThrow(), + "metafactory", + CD_CallSite, CD_MethodType, CD_MethodHandle, CD_MethodType); + + private static final DirectMethodHandleDesc DMHD_REFLECTABLE_LAMBDA_METAFACTORY = ofCallsiteBootstrap( + ReflectableLambdaMetafactory.class.describeConstable().orElseThrow(), + "metafactory", + CD_CallSite, CD_MethodType, CD_MethodHandle, CD_MethodType); + + private final MethodHandles.Lookup lookup; + private final Set names; + private final List functions = new ArrayList<>(); + private final LinkedHashMap modelsToBuild = new LinkedHashMap<>(); + private int nextLambdaIndex; + + private LambdaExpansionTransformer(MethodHandles.Lookup lookup, Set names) { + this.lookup = lookup; + this.names = new LinkedHashSet<>(names); + } + + static CoreOp.ModuleOp transform(MethodHandles.Lookup lookup, + SequencedMap ops) { + return new LambdaExpansionTransformer(lookup, ops.sequencedKeySet()).transform(ops); + } + + private CoreOp.ModuleOp transform(SequencedMap ops) { + for (var e : ops.sequencedEntrySet()) { + functions.add(switch (e.getValue()) { + case FuncOp fop -> fop.transform(e.getKey(), this); + case JavaOp.LambdaOp lop -> lambdaToFuncOp(e.getKey(), lop).transform(this); + default -> throw new IllegalArgumentException("Unsupported invokable operation: " + e.getValue()); + }); + } + if (!modelsToBuild.isEmpty()) { + functions.addAll(OpBuilder.createBuilderFunctions( + modelsToBuild, + b -> b.add(JavaOp.fieldLoad( + FieldRef.field(JavaOp.class, "JAVA_DIALECT_FACTORY", DialectFactory.class)))) + .functionTable().sequencedValues()); + } + return CoreOp.module(functions); + } + + // LambdaMetafactory implementation methods take captures before lambda parameters. + private static FuncOp lambdaToFuncOp(String name, JavaOp.LambdaOp lop) { + List captures = lop.capturedValues(); + FunctionType lambdaType = lop.invokableSignature(); + ArrayList parameterTypes = new ArrayList<>(captures.size() + lambdaType.parameterTypes().size()); + for (Value v : captures) { + parameterTypes.add(v.type() instanceof VarType vt ? vt.valueType() : v.type()); + } + parameterTypes.addAll(lambdaType.parameterTypes()); + return CoreOp.func(name, CoreType.functionType(lambdaType.returnType(), parameterTypes)).body(b -> { + int i = 0; + for (Value cv : captures) { + Value v = b.parameters().get(i++); + if (cv.type() instanceof VarType) { + v = b.add(CoreOp.var(v)); + } + b.context().mapValue(cv, v); + } + b.transformBody(lop.body(), b.parameters().subList(i, b.parameters().size()), + CodeTransformer.COPYING_TRANSFORMER); + }); + } + + private static String uniqueName(Set names, String name) { + if (names.add(name)) { + return name; + } + for (int i = 0; ; i++) { + String n = name + "$" + i; + if (names.add(n)) { + return n; + } + } + } + + @Override + public Block.Builder acceptOp(Block.Builder block, Op op) { + if (!(op instanceof JavaOp.LambdaOp lop)) { + block.add(op); + return block; + } + JavaType intfType = (JavaType) lop.functionalInterface(); + MethodTypeDesc mtd = MethodRef.toNominalDescriptor(lop.invokableSignature()); + try { + Class intfClass = (Class) intfType.erasure().resolve(lookup); + Method intfMethod = funcIntfMethod(intfClass, mtd); + List captures = lop.capturedValues(); + int i = nextLambdaIndex++; + String implName = uniqueName(names, "lambda$" + i); + String intfMethodName = intfMethod.getName(); + DirectMethodHandleDesc lambdaMetafactory = DMHD_LAMBDA_METAFACTORY; + if (lop.isReflectable()) { + String modelName = uniqueName(names, "op$lambda$" + i); + modelsToBuild.put(modelName, Quoted.embedOp(lop)); + lambdaMetafactory = DMHD_REFLECTABLE_LAMBDA_METAFACTORY; + intfMethodName = intfMethodName + "=" + modelName; + } + functions.add(lambdaToFuncOp(implName, lop).transform(this)); + + ClassDesc[] captureTypes = captures.stream() + .map(Value::type).map(LambdaExpansionTransformer::toClassDesc).toArray(ClassDesc[]::new); + Op.Result r = block.add(new DynamicFuncCallOp( + lop.functionalInterface(), + block.context().getValues(captures), + implName, + lambdaMetafactory, + intfMethodName, + MethodTypeDesc.of(intfType.toNominalDescriptor(), captureTypes), + MethodTypeDesc.of( + intfMethod.getReturnType().describeConstable().get(), + Stream.of(intfMethod.getParameterTypes()).map(t -> t.describeConstable().get()).toList()), + mtd)); + block.context().mapValue(lop.result(), r); + return block; + } catch (ReflectiveOperationException e) { + throw new IllegalArgumentException(e); + } + } + + private static ClassDesc toClassDesc(CodeType t) { + return switch (t) { + case VarType vt -> toClassDesc(vt.valueType()); + case JavaType jt -> jt.toNominalDescriptor(); + default -> throw new IllegalArgumentException("Bad type: " + t); + }; + } + + private static Method funcIntfMethod(Class intfc, MethodTypeDesc mtd) { + Method intfM = null; + for (Method m : intfc.getMethods()) { + String methodName = m.getName(); + if (Modifier.isAbstract(m.getModifiers()) + && (m.getReturnType() != String.class + || m.getParameterCount() != 0 + || !methodName.equals("toString")) + && (m.getReturnType() != int.class + || m.getParameterCount() != 0 + || !methodName.equals("hashCode")) + && (m.getReturnType() != boolean.class + || m.getParameterCount() != 1 + || m.getParameterTypes()[0] != Object.class + || !methodName.equals("equals"))) { + if (intfM == null) { + intfM = m; + } else if (!intfM.getName().equals(methodName)) { + throw new IllegalArgumentException("Not a single-method interface: " + intfc.getName()); + } + } + } + if (intfM == null) { + throw new IllegalArgumentException("No method in: " + intfc.getName() + " matching: " + mtd); + } + return intfM; + } +}