Path: blob/master/jcl/src/java.base/share/classes/java/lang/invoke/BruteArgumentMoverHandle.java
12513 views
/*[INCLUDE-IF Sidecar17 & !OPENJDK_METHODHANDLES]*/1/*******************************************************************************2* Copyright (c) 2013, 2020 IBM Corp. and others3*4* This program and the accompanying materials are made available under5* the terms of the Eclipse Public License 2.0 which accompanies this6* distribution and is available at https://www.eclipse.org/legal/epl-2.0/7* or the Apache License, Version 2.0 which accompanies this distribution and8* is available at https://www.apache.org/licenses/LICENSE-2.0.9*10* This Source Code may also be made available under the following11* Secondary Licenses when the conditions for such availability set12* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU13* General Public License, version 2 with the GNU Classpath14* Exception [1] and GNU General Public License, version 2 with the15* OpenJDK Assembly Exception [2].16*17* [1] https://www.gnu.org/software/classpath/license.html18* [2] http://openjdk.java.net/legal/assembly-exception.html19*20* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception21*******************************************************************************/22package java.lang.invoke;2324abstract class ArgumentMoverHandle extends PassThroughHandle {2526/** Base class for handles that can alter how arguments are passed to another handle.27* The "permute" array indicates what arguments to pass to the next handle,28* in what order. If this handle takes N arguments, then integers from29* 0..N-1 indicate that the corresponding argument to this handle should be30* passed on to the next handle. Any other integer results in a call to31* the "extra_" method of the right return type.32*33* For example, suppose you have an ArgumentMoverHandle "amh" taking 334* arguments and having a permute array of [2,25,1,1,-4,0]. That would mean35* that calling this:36*37* amh.invokeExact(x,y,z);38*39* is equivalent to this:40*41* amh.next.invokeExact(z, amh.extra_L(25), y, y, amd.extra_L(-4), x);42*43* ...assuming that all arguments are objects. (If they're not, a44* different extra_ method would be called.)45*46* Subclasses can implement the extra_ method in any way they want.47*/4849final MethodHandle next;50final int[] permute;5152protected ArgumentMoverHandle(MethodType type, MethodHandle next, int[] permute, Object infoAffectingThunks, MethodHandle equivalent) {53super(equivalent, infoAffectingThunks);54this.next = next;55this.permute = permute;56}5758protected ArgumentMoverHandle(ArgumentMoverHandle originalHandle, MethodType newType) {59super(originalHandle, newType);60this.next = originalHandle.next;61this.permute = originalHandle.permute;62}636465// {{{ JIT support6667protected ThunkTuple computeThunks(Object arg) {68return thunkTable().get(new ThunkKeyWithObjectArray(ThunkKey.computeThunkableType(type()), (Object[])arg));69}7071// This implements the argument permutation protocol.72// Ints 0..N-1 refer to the incoming arguments to the thunk; all other73// numbers are passed to the extra_X function returning the appropriate74// type, and subclasses can decide what they mean.75static native int permuteArgs(int argPlaceholder);7677// Thunks can only fabricate static calls, but we want virtual calls,78// so here are a bunch of wrappers.79private static boolean extra_Z(ArgumentMoverHandle handle, int index){ return handle.extra_Z(index); }80private static byte extra_B(ArgumentMoverHandle handle, int index){ return handle.extra_B(index); }81private static char extra_C(ArgumentMoverHandle handle, int index){ return handle.extra_C(index); }82private static short extra_S(ArgumentMoverHandle handle, int index){ return handle.extra_S(index); }83private static int extra_I(ArgumentMoverHandle handle, int index){ return handle.extra_I(index); }84private static long extra_J(ArgumentMoverHandle handle, int index){ return handle.extra_J(index); }85private static float extra_F(ArgumentMoverHandle handle, int index){ return handle.extra_F(index); }86private static double extra_D(ArgumentMoverHandle handle, int index){ return handle.extra_D(index); }87private static Object extra_L(ArgumentMoverHandle handle, int index){ return handle.extra_L(index); }8889// Subclasses can implement whichever of these they need90native boolean extra_Z(int index);91native byte extra_B(int index);92native char extra_C(int index);93native short extra_S(int index);94native int extra_I(int index);95native long extra_J(int index);96native float extra_F(int index);97native double extra_D(int index);98native Object extra_L(int index);99100// }}} JIT support101102void compareWithArgumentMover(ArgumentMoverHandle left, Comparator c) {103c.compareStructuralParameter(left.permute.length, this.permute.length);104for (int i = 0; (i < left.permute.length) && (i < this.permute.length); i++) {105c.compareStructuralParameter(left.permute[i], this.permute[i]);106}107c.compareChildHandle(left.next, this.next);108}109110}111112final class BruteArgumentMoverHandle extends ArgumentMoverHandle {113114/** An ArgumentMoverHandle that can hold additional values to pass to the next handle.115* The "extra" array contains the values to pass along. In addition, for116* performance reasons, the first few arguments are also redundantly stored117* in fields of the BruteArgumentMoverHandle object, which allows us to118* avoid unboxing primitives, and also reduces the number of levels of119* indirection needed to access objects.120*121* The extra_ methods take an integer i from -1 to -N, where N is the122* length of the extra array. They return extra[-1-i], or a faster equivalent.123*124* This functionality is enough to compose any number and sequence of125* insertArguments, permuteArguments, and dropArguments operations, as well126* as asType operations that do only boxing / unboxing and integer widening.127* This is the main "value add" of ArgumentMoverHandle: it collapses whole128* chains of these handles into a single handle, most of whose functionality129* is performed at creation time instead of invocation time.130*131* (Generally, other asType operations need AsType handles because they are132* not composable. For example, casting an argument to class A and then to133* B actually does necessitate two checkcasts if A and B are unrelated types.)134*/135136final Object[] extra;137138// Save a level of indirection for the first few objects.139final Object extra_L0;140final Object extra_L1;141final Object extra_L2;142final Object extra_L3;143final Object extra_L4;144145// Save a couple of levels of indirection for inserted ints.146// Also, mark as non-final so we leave the loads in the residual code.147// They can be treated as honourary finals in full-custom thunks.148int extra_I0;149int extra_I1;150int extra_I2;151int extra_I3;152int extra_I4;153154// Longs too.155long extra_J0;156long extra_J1;157long extra_J2;158long extra_J3;159long extra_J4;160161protected BruteArgumentMoverHandle(MethodType type, MethodHandle next, int[] permute, Object[] extra, MethodHandle equivalent) {162super(type, next, permute, infoAffectingThunks(next, permute, extra), equivalent);163this.extra = extra;164// This code is a bit contorted just to satisfy javac165if (extra.length >= 1) {166extra_L0 = extra[0];167if (extra_L0 instanceof Integer) {168extra_I0 = (Integer)extra_L0;169} else if (extra_L0 instanceof Long) {170extra_J0 = (Long)extra_L0;171}172} else {173extra_L0 = null;174}175if (extra.length >= 2) {176extra_L1 = extra[1];177if (extra_L1 instanceof Integer) {178extra_I1 = (Integer)extra_L1;179} else if (extra_L1 instanceof Long) {180extra_J1 = (Long)extra_L1;181}182} else {183extra_L1 = null;184}185if (extra.length >= 3) {186extra_L2 = extra[2];187if (extra_L2 instanceof Integer) {188extra_I2 = (Integer)extra_L2;189} else if (extra_L2 instanceof Long) {190extra_J2 = (Long)extra_L2;191}192} else {193extra_L2 = null;194}195if (extra.length >= 4) {196extra_L3 = extra[3];197if (extra_L3 instanceof Integer) {198extra_I3 = (Integer)extra_L3;199} else if (extra_L3 instanceof Long) {200extra_J3 = (Long)extra_L3;201}202} else {203extra_L3 = null;204}205if (extra.length >= 5) {206extra_L4 = extra[4];207if (extra_L4 instanceof Integer) {208extra_I4 = (Integer)extra_L4;209} else if (extra_L4 instanceof Long) {210extra_J4 = (Long)extra_L4;211}212} else {213extra_L4 = null;214}215}216217protected BruteArgumentMoverHandle(BruteArgumentMoverHandle originalHandle, MethodType newType) {218super(originalHandle, newType);219extra = originalHandle.extra;220extra_L0 = originalHandle.extra_L0;221extra_L1 = originalHandle.extra_L1;222extra_L2 = originalHandle.extra_L2;223extra_L3 = originalHandle.extra_L3;224extra_L4 = originalHandle.extra_L4;225extra_I0 = originalHandle.extra_I0;226extra_I1 = originalHandle.extra_I1;227extra_I2 = originalHandle.extra_I2;228extra_I3 = originalHandle.extra_I3;229extra_I4 = originalHandle.extra_I4;230extra_J0 = originalHandle.extra_J0;231extra_J1 = originalHandle.extra_J1;232extra_J2 = originalHandle.extra_J2;233extra_J3 = originalHandle.extra_J3;234extra_J4 = originalHandle.extra_J4;235}236237@Override238MethodHandle cloneWithNewType(MethodType newType) {239return new BruteArgumentMoverHandle(this, newType);240}241242static int[] identityPermute(MethodType type) {243int[] result = new int[type.parameterCount()];244for (int i = 0; i < result.length; i++) {245result[i] = i;246}247return result;248}249250static int[] composePermute(int[] inner, int[] outer, int outerExtraIndexOffset) {251int innerLength = inner.length;252int[] result = new int[innerLength];253for (int i = 0; i < innerLength; i++) {254int index = inner[i];255if ((0 <= index) && (index < outer.length)) {256result[i] = outer[inner[i]];257} else {258result[i] = index + outerExtraIndexOffset;259}260}261return result;262}263264static int[] insertPermute(int[] originalPermute, int insertLocation, int numValues, int startingIndex) {265// TODO: A variant that takes a MethodType instead of originalPermute so we don't have to bother with identityPermute266int[] result = new int[originalPermute.length + numValues];267for (int i = 0; i < insertLocation; i++) {268result[i] = originalPermute[i];269}270for (int i = 0; i < numValues; i++) {271result[insertLocation + i] = startingIndex - i;272}273for (int i = insertLocation; i < originalPermute.length; i++) {274result[i + numValues] = originalPermute[i];275}276return result;277}278279final MethodHandle permuteArguments(MethodType permuteType, int... outerPermute) throws NullPointerException, IllegalArgumentException {280if (isUnnecessaryPermute(permuteType, outerPermute)) {281return this;282}283284return new BruteArgumentMoverHandle(285permuteType,286next,287composePermute(this.permute, outerPermute, 0),288extra,289new PermuteHandle(permuteType, this.equivalent, outerPermute)290);291}292293final MethodHandle insertArguments(MethodHandle equivalent, MethodHandle unboxingHandle, int location, Object... outerValues) {294MethodHandle result;295int[] insertPermute = insertPermute(identityPermute(equivalent.type), location, outerValues.length, -1);296int[] combinedPermute = composePermute(this.permute, insertPermute, -outerValues.length);297Object[] combinedExtra;298if (this.extra.length >= 1) {299combinedExtra = java.util.Arrays.copyOf(outerValues, outerValues.length + this.extra.length);300System.arraycopy(this.extra, 0, combinedExtra, outerValues.length, this.extra.length);301} else {302combinedExtra = outerValues;303}304result = new BruteArgumentMoverHandle(305equivalent.type(),306next,307combinedPermute,308combinedExtra,309equivalent310);311return result;312}313314// {{{ JIT support315316private static final ThunkTable _thunkTable = new ThunkTable();317protected ThunkTable thunkTable(){ return _thunkTable; }318319final Object extra_L(int index) {320if (index == -1) {321return extra_L0;322} else if (index == -2) {323return extra_L1;324} else if (index == -3) {325return extra_L2;326} else if (index == -4) {327return extra_L3;328} else if (index == -5) {329return extra_L4;330} else {331return extra[-1 - index];332}333}334335final int extra_I(int index) {336if (index == -1) {337return extra_I0;338} else if (index == -2) {339return extra_I1;340} else if (index == -3) {341return extra_I2;342} else if (index == -4) {343return extra_I3;344} else if (index == -5) {345return extra_I4;346} else {347return (Integer)extra[-1 - index];348}349}350351final long extra_J(int index) {352if (index == -1) {353return extra_J0;354} else if (index == -2) {355return extra_J1;356} else if (index == -3) {357return extra_J2;358} else if (index == -4) {359return extra_J3;360} else if (index == -5) {361return extra_J4;362} else {363return (Long)extra[-1 - index];364}365}366367// Unbox if needed. These allow us to skip an AsTypeHandle just for unboxing,368// though it does impose a checkcast unless the jit can eliminate it.369//370final boolean extra_Z(int index) {371return (Boolean)extra_L(index);372}373final byte extra_B(int index) {374return (Byte)extra_L(index); }375final short extra_S(int index) {376return (Short)extra_L(index); }377final char extra_C(int index) {378return (Character)extra_L(index);379}380final float extra_F(int index) {381return (Float)extra_L(index);382}383final double extra_D(int index) {384return (Double)extra_L(index);385}386387private static Object[] infoAffectingThunks(MethodHandle next, int[] permute, Object[] extra) {388// The location and number of values to insert affects the code generated in shareable thunks,389// as does the thunkableType of the next handle.390// The actual inserted values don't affect shareable thunks.391Object[] result = {ThunkKey.computeThunkableType(next.type()), permute};392return result;393}394395static native int permuteArgs(int argPlaceholder, Object extra_L0, Object extra_L1, Object extra_L2, Object extra_L3, Object extra_L4, Object[] extra);396397@FrameIteratorSkip398private final int invokeExact_thunkArchetype_X(int argPlaceholder) {399if (ILGenMacros.isShareableThunk()) {400undoCustomizationLogic(next);401}402if (!ILGenMacros.isCustomThunk()) {403doCustomizationLogic();404}405return ILGenMacros.invokeExact_X(406next,407permuteArgs(argPlaceholder, this.extra_L0, this.extra_L1, this.extra_L2, this.extra_L3, this.extra_L4, this.extra));408}409410// }}} JIT support411final void compareWith(MethodHandle right, Comparator c) {412if (right instanceof BruteArgumentMoverHandle) {413((BruteArgumentMoverHandle)right).compareWithBruteArgumentMover(this, c);414} else {415c.fail();416}417}418419final void compareWithArgumentMover(ArgumentMoverHandle left, Comparator c) {420// If left were an BruteArgumentMoverHandle, we'd be in421// compareWithBruteArgumentMover, so it doesn't match.422c.fail();423}424425final void compareWithBruteArgumentMover(BruteArgumentMoverHandle left, Comparator c) {426c.compareStructuralParameter(left.extra.length, this.extra.length);427for (int i = 0; (i < left.extra.length) && (i < this.extra.length); i++) {428c.compareUserSuppliedParameter(left.extra[i], this.extra[i]);429}430super.compareWithArgumentMover(left, c);431}432433}434435436