/*
 * Copyright (c) 2020, 2020, 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 com.oracle.svm.methodhandles;

import static com.oracle.svm.core.annotate.TargetElement.CONSTRUCTOR_NAME;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.Arrays;

import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.Inject;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;

In the JDK implementation of method handles, each bound method handle is an instance of a specific species class, depending on the types of the parameters bound to the handle. For example, a simple MethodHandle.bindTo(Object) call on a direct method handle will be represented by a bound method handle of species "L", containing a single bound parameter named argL0. To be able to handle every possible bound method handle species, the JDK implementation provides a set of pre-compiled species for the most common cases, and generates the other ones at runtime the first time they are required (see Target_java_lang_invoke_ClassSpecializer). The Native Image implementation of bound method handles does not follow this design. Instead, it represents every bound method handle as an instance of the BoundMethodHandle class, where its bound parameters are stored in an injected boxed array. This design choice allows a single implementation of the methods that every species must provide, namely the make method (factory) and the copyWith and copyWithExtend* methods (copy constructors).
/** * In the JDK implementation of method handles, each bound method handle is an instance of a * specific species class, depending on the types of the parameters bound to the handle. For * example, a simple {@link MethodHandle#bindTo(Object)} call on a direct method handle will be * represented by a bound method handle of species "L", containing a single bound parameter named * argL0. * * To be able to handle every possible bound method handle species, the JDK implementation provides * a set of pre-compiled species for the most common cases, and generates the other ones at runtime * the first time they are required (see {@link Target_java_lang_invoke_ClassSpecializer}). * * The Native Image implementation of bound method handles does not follow this design. Instead, it * represents every bound method handle as an instance of the BoundMethodHandle class, where its * bound parameters are stored in an injected boxed array. This design choice allows a single * implementation of the methods that every species must provide, namely the make method (factory) * and the copyWith and copyWithExtend* methods (copy constructors). */
@TargetClass(className = "java.lang.invoke.BoundMethodHandle", onlyWith = MethodHandlesSupported.class) final class Target_java_lang_invoke_BoundMethodHandle { @Alias static Target_java_lang_invoke_BoundMethodHandle_Specializer SPECIALIZER; @Alias @TargetElement(name = CONSTRUCTOR_NAME) native void constructor(MethodType type, Target_java_lang_invoke_LambdaForm form); @Alias static native Target_java_lang_invoke_BoundMethodHandle_SpeciesData speciesDataFor(Target_java_lang_invoke_LambdaForm form); } /* * We hijack the species with no bound parameters for our implementation since it already inherits * from BoundMethodHandle and doesn't contain any superfluous members. */ @TargetClass(className = "java.lang.invoke.SimpleMethodHandle", onlyWith = MethodHandlesSupported.class) final class Target_java_lang_invoke_SimpleMethodHandle { /* * Since we represent all the bound method handle species with the basic one, the species data * is therefore an instance field of the class, not a static one. */ @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// Target_java_lang_invoke_BoundMethodHandle_SpeciesData speciesData; @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// Object[] args; @Substitute Target_java_lang_invoke_SimpleMethodHandle(MethodType type, Target_java_lang_invoke_LambdaForm form) { SubstrateUtil.cast(this, Target_java_lang_invoke_BoundMethodHandle.class).constructor(type, form); } @Substitute Target_java_lang_invoke_BoundMethodHandle_SpeciesData speciesData() { if (speciesData == null) { speciesData = Target_java_lang_invoke_BoundMethodHandle.speciesDataFor(SubstrateUtil.cast(this, Target_java_lang_invoke_MethodHandle.class).internalForm()); } return speciesData; } @Substitute Target_java_lang_invoke_BoundMethodHandle copyWithExtendL(MethodType type, Target_java_lang_invoke_LambdaForm form, Object newArg) { return BoundMethodHandleUtils.make(type, form, BoundMethodHandleUtils.speciesKey(this) + "L", BoundMethodHandleUtils.appendArgs(args, newArg)); } @Substitute Target_java_lang_invoke_BoundMethodHandle copyWithExtendI(MethodType type, Target_java_lang_invoke_LambdaForm form, int newArg) { return BoundMethodHandleUtils.make(type, form, BoundMethodHandleUtils.speciesKey(this) + "I", BoundMethodHandleUtils.appendArgs(args, newArg)); } @Substitute Target_java_lang_invoke_BoundMethodHandle copyWithExtendJ(MethodType type, Target_java_lang_invoke_LambdaForm form, long newArg) { return BoundMethodHandleUtils.make(type, form, BoundMethodHandleUtils.speciesKey(this) + "J", BoundMethodHandleUtils.appendArgs(args, newArg)); } @Substitute Target_java_lang_invoke_BoundMethodHandle copyWithExtendF(MethodType type, Target_java_lang_invoke_LambdaForm form, float newArg) { return BoundMethodHandleUtils.make(type, form, BoundMethodHandleUtils.speciesKey(this) + "F", BoundMethodHandleUtils.appendArgs(args, newArg)); } @Substitute Target_java_lang_invoke_BoundMethodHandle copyWithExtendD(MethodType type, Target_java_lang_invoke_LambdaForm form, double newArg) { return BoundMethodHandleUtils.make(type, form, BoundMethodHandleUtils.speciesKey(this) + "D", BoundMethodHandleUtils.appendArgs(args, newArg)); } @Substitute Target_java_lang_invoke_BoundMethodHandle copyWith(MethodType type, Target_java_lang_invoke_LambdaForm form) { return BoundMethodHandleUtils.make(type, form, BoundMethodHandleUtils.speciesKey(this), args); } } /* Hardcoded species, needs a special case to avoid initialization */ @TargetClass(className = "java.lang.invoke.BoundMethodHandle", innerClass = "Species_L", onlyWith = MethodHandlesSupported.class) final class Target_java_lang_invoke_BoundMethodHandle_Species_L { @Substitute static Target_java_lang_invoke_BoundMethodHandle make(MethodType mt, Target_java_lang_invoke_LambdaForm lf, Object argL0) { return BoundMethodHandleUtils.make(mt, lf, "L", argL0); } } @TargetClass(className = "java.lang.invoke.BoundMethodHandle", innerClass = "SpeciesData", onlyWith = MethodHandlesSupported.class) final class Target_java_lang_invoke_BoundMethodHandle_SpeciesData { } @TargetClass(className = "java.lang.invoke.BoundMethodHandle", innerClass = "Specializer", onlyWith = MethodHandlesSupported.class) final class Target_java_lang_invoke_BoundMethodHandle_Specializer { } final class BoundMethodHandleUtils { /* Bound method handle constructor */ static Target_java_lang_invoke_BoundMethodHandle make(MethodType type, Target_java_lang_invoke_LambdaForm form, String species, Object... args) { Target_java_lang_invoke_SimpleMethodHandle bmh = new Target_java_lang_invoke_SimpleMethodHandle(type, form); bmh.speciesData = SubstrateUtil.cast(Target_java_lang_invoke_BoundMethodHandle.SPECIALIZER, Target_java_lang_invoke_ClassSpecializer.class).findSpecies(species); bmh.args = (args != null) ? Arrays.copyOf(args, args.length) : new Object[0]; return SubstrateUtil.cast(bmh, Target_java_lang_invoke_BoundMethodHandle.class); } static Object[] appendArgs(Object[] args, Object newArg) { if (args == null) { return new Object[]{newArg}; } Object[] newArgs = new Object[args.length + 1]; System.arraycopy(args, 0, newArgs, 0, args.length); newArgs[args.length] = newArg; return newArgs; } static String speciesKey(Target_java_lang_invoke_SimpleMethodHandle bmh) { return SubstrateUtil.cast(bmh.speciesData(), Target_java_lang_invoke_ClassSpecializer_SpeciesData.class).key(); } }