1 /*
  2  * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package java.lang.invoke;
 27 
 28 import jdk.internal.vm.annotation.Stable;
 29 
 30 import java.util.ArrayList;
 31 import java.util.List;
 32 
 33 import static java.lang.invoke.LambdaForm.BasicType;
 34 import static java.lang.invoke.LambdaForm.BasicType.*;
 35 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
 36 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 37 import static java.lang.invoke.MethodHandleStatics.newInternalError;
 38 import static java.lang.invoke.MethodHandleStatics.uncaughtException;
 39 
 40 /**
 41  * The flavor of method handle which emulates an invoke instruction
 42  * on a predetermined argument.  The JVM dispatches to the correct method
 43  * when the handle is created, not when it is invoked.
 44  *
 45  * All bound arguments are encapsulated in dedicated species.
 46  */
 47 /*non-public*/
 48 abstract non-sealed class BoundMethodHandle extends MethodHandle {
 49 
 50     /*non-public*/
 51     BoundMethodHandle(MethodType type, LambdaForm form) {
 52         super(type, form);
 53         assert(speciesData() == speciesDataFor(form));
 54     }
 55 
 56     //
 57     // BMH API and internals
 58     //
 59 
 60     /*non-public*/
 61     LambdaFormEditor editor() {
 62         return form.editor();
 63     }
 64 
 65     static BoundMethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
 66         return Species_L.make(type, form, x);
 67     }
 68 
 69     @Override // there is a default binder in the super class, for 'L' types only
 70     /*non-public*/
 71     BoundMethodHandle bindArgumentL(int pos, Object value) {
 72         return editor().bindArgumentL(this, pos, value);
 73     }
 74 
 75     /*non-public*/
 76     BoundMethodHandle bindArgumentI(int pos, int value) {
 77         return editor().bindArgumentI(this, pos, value);
 78     }
 79     /*non-public*/
 80     BoundMethodHandle bindArgumentJ(int pos, long value) {
 81         return editor().bindArgumentJ(this, pos, value);
 82     }
 83     /*non-public*/
 84     BoundMethodHandle bindArgumentF(int pos, float value) {
 85         return editor().bindArgumentF(this, pos, value);
 86     }
 87     /*non-public*/
 88     BoundMethodHandle bindArgumentD(int pos, double value) {
 89         return editor().bindArgumentD(this, pos, value);
 90     }
 91     @Override
 92     BoundMethodHandle rebind() {
 93         if (!tooComplex()) {
 94             return this;
 95         }
 96         return makeReinvoker(this);
 97     }
 98 
 99     private boolean tooComplex() {
100         return (fieldCount() > FIELD_COUNT_THRESHOLD ||
101                 form.expressionCount() > FORM_EXPRESSION_THRESHOLD);
102     }
103     private static final int FIELD_COUNT_THRESHOLD = 12;      // largest convenient BMH field count
104     private static final int FORM_EXPRESSION_THRESHOLD = 24;  // largest convenient BMH expression count
105 
106     /**
107      * A reinvoker MH has this form:
108      * {@code lambda (bmh, arg*) { thismh = bmh[0]; invokeBasic(thismh, arg*) }}
109      */
110     static BoundMethodHandle makeReinvoker(MethodHandle target) {
111         LambdaForm form = DelegatingMethodHandle.makeReinvokerForm(
112                 target, MethodTypeForm.LF_REBIND,
113                 Species_L.BMH_SPECIES, Species_L.BMH_SPECIES.getterFunction(0));
114         return Species_L.make(target.type(), form, target);
115     }
116 
117     /**
118      * Return the {@link BoundMethodHandle.SpeciesData} instance representing this BMH species. All subclasses must provide a
119      * static field containing this value, and they must accordingly implement this method.
120      */
121     /*non-public*/
122     abstract BoundMethodHandle.SpeciesData speciesData();
123 
124     /*non-public*/
125     static BoundMethodHandle.SpeciesData speciesDataFor(LambdaForm form) {
126         Object c = form.names[0].constraint;
127         if (c instanceof SpeciesData sd) {
128             return sd;
129         }
130         // if there is no BMH constraint, then use the null constraint
131         return SPECIALIZER.topSpecies();
132     }
133 
134     /**
135      * Return the number of fields in this BMH.  Equivalent to speciesData().fieldCount().
136      */
137     /*non-public*/
138     final int fieldCount() { return speciesData().fieldCount(); }
139 
140     @Override
141     Object internalProperties(int indentLevel) {
142         return "\n" + debugPrefix(indentLevel) + "& BMH=" + internalValues(indentLevel);
143     }
144 
145     @Override
146     final String internalValues(int indentLevel) {
147         String prefix = debugPrefix(indentLevel);
148         int count = fieldCount();
149         if (count == 1 && indentLevel < 0) {
150             return "[" + arg(0) + "]";
151         }
152         StringBuilder sb = new StringBuilder("[");
153         for (int i = 0; i < count; ++i) {
154             Object theArg = arg(i);
155             sb.append("\n  ").append(prefix).append(i);
156             if (indentLevel >= 0 && theArg instanceof MethodHandle mh) {
157                 sb.append(": MethodHandle = {").append(mh.debugString(indentLevel+1));
158                 sb.append("\n  ").append(prefix).append("}");
159             } else {
160                 sb.append(": ( ").append(theArg).append(" )");
161             }
162         }
163         return sb.append("\n").append(prefix).append("]").toString();
164     }
165 
166     /*non-public*/
167     final Object arg(int i) {
168         try {
169             Class<?> fieldType = speciesData().fieldTypes().get(i);
170             switch (BasicType.basicType(fieldType)) {
171                 case L_TYPE: return          speciesData().getter(i).invokeBasic(this);
172                 case I_TYPE: return (int)    speciesData().getter(i).invokeBasic(this);
173                 case J_TYPE: return (long)   speciesData().getter(i).invokeBasic(this);
174                 case F_TYPE: return (float)  speciesData().getter(i).invokeBasic(this);
175                 case D_TYPE: return (double) speciesData().getter(i).invokeBasic(this);
176             }
177         } catch (Throwable ex) {
178             throw uncaughtException(ex);
179         }
180         throw new InternalError("unexpected type: " + speciesData().key()+"."+i);
181     }
182 
183     //
184     // cloning API
185     //
186 
187     /*non-public*/
188     abstract BoundMethodHandle copyWith(MethodType mt, LambdaForm lf);
189     /*non-public*/
190     abstract BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg);
191     /*non-public*/
192     abstract BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int    narg);
193     /*non-public*/
194     abstract BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long   narg);
195     /*non-public*/
196     abstract BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float  narg);
197     /*non-public*/
198     abstract BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg);
199 
200     //
201     // concrete BMH classes required to close bootstrap loops
202     //
203 
204     private  // make it private to force users to access the enclosing class first
205     static final class Species_L extends BoundMethodHandle {
206 
207         final Object argL0;
208 
209         private Species_L(MethodType mt, LambdaForm lf, Object argL0) {
210             super(mt, lf);
211             this.argL0 = argL0;
212         }
213 
214         @Override
215         /*non-public*/
216         SpeciesData speciesData() {
217             return BMH_SPECIES;
218         }
219 
220         /*non-public*/
221         static @Stable SpeciesData BMH_SPECIES;
222 
223         /*non-public*/
224         static BoundMethodHandle make(MethodType mt, LambdaForm lf, Object argL0) {
225             return new Species_L(mt, lf, argL0);
226         }
227         @Override
228         /*non-public*/
229         final BoundMethodHandle copyWith(MethodType mt, LambdaForm lf) {
230             return new Species_L(mt, lf, argL0);
231         }
232         @Override
233         /*non-public*/
234         final BoundMethodHandle copyWithExtendL(MethodType mt, LambdaForm lf, Object narg) {
235             try {
236                 return (BoundMethodHandle) BMH_SPECIES.extendWith(L_TYPE).factory().invokeBasic(mt, lf, argL0, narg);
237             } catch (Throwable ex) {
238                 throw uncaughtException(ex);
239             }
240         }
241         @Override
242         /*non-public*/
243         final BoundMethodHandle copyWithExtendI(MethodType mt, LambdaForm lf, int narg) {
244             try {
245                 return (BoundMethodHandle) BMH_SPECIES.extendWith(I_TYPE).factory().invokeBasic(mt, lf, argL0, narg);
246             } catch (Throwable ex) {
247                 throw uncaughtException(ex);
248             }
249         }
250         @Override
251         /*non-public*/
252         final BoundMethodHandle copyWithExtendJ(MethodType mt, LambdaForm lf, long narg) {
253             try {
254                 return (BoundMethodHandle) BMH_SPECIES.extendWith(J_TYPE).factory().invokeBasic(mt, lf, argL0, narg);
255             } catch (Throwable ex) {
256                 throw uncaughtException(ex);
257             }
258         }
259         @Override
260         /*non-public*/
261         final BoundMethodHandle copyWithExtendF(MethodType mt, LambdaForm lf, float narg) {
262             try {
263                 return (BoundMethodHandle) BMH_SPECIES.extendWith(F_TYPE).factory().invokeBasic(mt, lf, argL0, narg);
264             } catch (Throwable ex) {
265                 throw uncaughtException(ex);
266             }
267         }
268         @Override
269         /*non-public*/
270         final BoundMethodHandle copyWithExtendD(MethodType mt, LambdaForm lf, double narg) {
271             try {
272                 return (BoundMethodHandle) BMH_SPECIES.extendWith(D_TYPE).factory().invokeBasic(mt, lf, argL0, narg);
273             } catch (Throwable ex) {
274                 throw uncaughtException(ex);
275             }
276         }
277     }
278 
279     //
280     // BMH species meta-data
281     //
282 
283     /*non-public*/
284     static final class SpeciesData
285             extends ClassSpecializer<BoundMethodHandle, String, SpeciesData>.SpeciesData {
286         // This array is filled in lazily, as new species come into being over time.
287         @Stable private final SpeciesData[] extensions = new SpeciesData[ARG_TYPE_LIMIT];
288 
289         public SpeciesData(Specializer outer, String key) {
290             outer.super(key);
291         }
292 
293         @Override
294         protected String deriveClassName() {
295             String typeString = deriveTypeString();
296             if (typeString.isEmpty()) {
297                 return SimpleMethodHandle.class.getName();
298             }
299             return BoundMethodHandle.class.getName() + "$Species_" + typeString;
300         }
301 
302         @Override
303         protected List<Class<?>> deriveFieldTypes(String key) {
304             ArrayList<Class<?>> types = new ArrayList<>(key.length());
305             for (int i = 0; i < key.length(); i++) {
306                 types.add(basicType(key.charAt(i)).basicTypeClass());
307             }
308             return types;
309         }
310 
311         @Override
312         protected String deriveTypeString() {
313             // (If/when we have to add nominal types, just inherit the more complex default.)
314             return key();
315         }
316 
317         @Override
318         protected MethodHandle deriveTransformHelper(MemberName transform, int whichtm) {
319             if (whichtm == Specializer.TN_COPY_NO_EXTEND) {
320                 return factory();
321             } else if (whichtm < ARG_TYPE_LIMIT) {
322                 return extendWith(BasicType.basicType((byte) whichtm)).factory();
323             } else {
324                 throw newInternalError("bad transform");
325             }
326         }
327 
328         @Override
329         protected <X> List<X> deriveTransformHelperArguments(MemberName transform, int whichtm, List<X> args, List<X> fields) {
330             assert(verifyTHAargs(transform, whichtm, args, fields));
331             // The rule is really simple:  Keep the first two arguments
332             // the same, then put in the fields, then put any other argument.
333             args.addAll(2, fields);
334             return args;
335         }
336 
337         private boolean verifyTHAargs(MemberName transform, int whichtm, List<?> args, List<?> fields) {
338             assert(transform == Specializer.BMH_TRANSFORMS.get(whichtm));
339             MethodType tType = transform.getMethodType();
340             assert(args.size() == tType.parameterCount());
341             assert(fields.size() == this.fieldCount());
342             final int MH_AND_LF = 2;
343             if (whichtm == Specializer.TN_COPY_NO_EXTEND) {
344                 assert(tType.parameterCount() == MH_AND_LF);
345             } else if (whichtm < ARG_TYPE_LIMIT) {
346                 assert(tType.parameterCount() == MH_AND_LF+1);
347                 final BasicType type = basicType((byte) whichtm);
348                 assert(tType.parameterType(MH_AND_LF) == type.basicTypeClass());
349             } else {
350                 return false;
351             }
352             return true;
353         }
354 
355         /*non-public*/
356         SpeciesData extendWith(BasicType basicType) {
357             int typeNum = basicType.ordinal();
358             SpeciesData sd = extensions[typeNum];
359             if (sd != null)  return sd;
360             sd = SPECIALIZER.findSpecies(key() + basicType.basicTypeChar());
361             extensions[typeNum] = sd;
362             return sd;
363         }
364     }
365 
366     static class AOTHolder {
367         static final Specializer SPECIALIZER = new Specializer();
368         static final SpeciesData SimpleMethodHandle_BMH_SPECIES = SPECIALIZER.findSpecies("");
369         static final SpeciesData Species_L_BMH_SPECIES = SPECIALIZER.findSpecies("L");
370     }
371 
372     /*non-public*/
373     static final Specializer SPECIALIZER = AOTHolder.SPECIALIZER;
374     static {
375         SimpleMethodHandle.BMH_SPECIES = AOTHolder.SimpleMethodHandle_BMH_SPECIES;
376         Species_L.BMH_SPECIES = AOTHolder.Species_L_BMH_SPECIES;
377     }
378 
379     /*non-public*/
380     static final class Specializer
381             extends ClassSpecializer<BoundMethodHandle, String, SpeciesData> {
382 
383         private static final MemberName SPECIES_DATA_ACCESSOR;
384 
385         static {
386             try {
387                 SPECIES_DATA_ACCESSOR = IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BoundMethodHandle.class,
388                         "speciesData", MethodType.methodType(BoundMethodHandle.SpeciesData.class));
389             } catch (ReflectiveOperationException ex) {
390                 throw newInternalError("Bootstrap link error", ex);
391             }
392         }
393 
394         private Specializer() {
395             super(  // Reified type parameters:
396                     BoundMethodHandle.class, String.class, BoundMethodHandle.SpeciesData.class,
397                     // Principal constructor type:
398                     MethodType.methodType(void.class, MethodType.class, LambdaForm.class),
399                     // Required linkage between class and species:
400                     SPECIES_DATA_ACCESSOR,
401                     "BMH_SPECIES",
402                     BMH_TRANSFORMS);
403         }
404 
405         @Override
406         protected String topSpeciesKey() {
407             return "";
408         }
409 
410         @Override
411         protected BoundMethodHandle.SpeciesData newSpeciesData(String key) {
412             return new BoundMethodHandle.SpeciesData(this, key);
413         }
414 
415         static final List<MemberName> BMH_TRANSFORMS;
416         static final int TN_COPY_NO_EXTEND = V_TYPE.ordinal();
417         static {
418             final Class<BoundMethodHandle> BMH = BoundMethodHandle.class;
419             // copyWithExtendLIJFD + copyWith
420             try {
421                 BMH_TRANSFORMS = List.of(
422                         IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendL", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, Object.class)),
423                         IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendI", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, int.class)),
424                         IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendJ", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, long.class)),
425                         IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendF", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, float.class)),
426                         IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWithExtendD", MethodType.methodType(BMH, MethodType.class, LambdaForm.class, double.class)),
427                         IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, BMH, "copyWith", MethodType.methodType(BMH, MethodType.class, LambdaForm.class))
428                 );
429             } catch (ReflectiveOperationException ex) {
430                 throw newInternalError("Failed resolving copyWith methods", ex);
431             }
432 
433             // as it happens, there is one transform per BasicType including V_TYPE
434             assert(BMH_TRANSFORMS.size() == TYPE_LIMIT);
435         }
436 
437         /**
438          * Generation of concrete BMH classes.
439          *
440          * A concrete BMH species is fit for binding a number of values adhering to a
441          * given type pattern. Reference types are erased.
442          *
443          * BMH species are cached by type pattern.
444          *
445          * A BMH species has a number of fields with the concrete (possibly erased) types of
446          * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
447          * which can be included as names in lambda forms.
448          */
449         class Factory extends ClassSpecializer<BoundMethodHandle, String, BoundMethodHandle.SpeciesData>.Factory {
450             @Override
451             protected String chooseFieldName(Class<?> type, int index) {
452                 return "arg" + super.chooseFieldName(type, index);
453             }
454         }
455 
456         @Override
457         protected Factory makeFactory() {
458             return new Factory();
459         }
460       }
461 
462     static SpeciesData speciesData_L()      { return Species_L.BMH_SPECIES; }
463     static SpeciesData speciesData_LL()     { return SPECIALIZER.findSpecies("LL"); }
464     static SpeciesData speciesData_LLL()    { return SPECIALIZER.findSpecies("LLL"); }
465     static SpeciesData speciesData_LLLL()   { return SPECIALIZER.findSpecies("LLLL"); }
466     static SpeciesData speciesData_LLLLL()  { return SPECIALIZER.findSpecies("LLLLL"); }
467 }