rt/emul/compact/src/main/java/java/lang/invoke/MethodTypeForm.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 09 Aug 2014 11:11:13 +0200
branchjdk8-b132
changeset 1646 c880a8a8803b
permissions -rw-r--r--
Batch of classes necessary to implement invoke dynamic interfaces. Taken from JDK8 build 132
jaroslav@1646
     1
/*
jaroslav@1646
     2
 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
jaroslav@1646
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jaroslav@1646
     4
 *
jaroslav@1646
     5
 * This code is free software; you can redistribute it and/or modify it
jaroslav@1646
     6
 * under the terms of the GNU General Public License version 2 only, as
jaroslav@1646
     7
 * published by the Free Software Foundation.  Oracle designates this
jaroslav@1646
     8
 * particular file as subject to the "Classpath" exception as provided
jaroslav@1646
     9
 * by Oracle in the LICENSE file that accompanied this code.
jaroslav@1646
    10
 *
jaroslav@1646
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
jaroslav@1646
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jaroslav@1646
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
jaroslav@1646
    14
 * version 2 for more details (a copy is included in the LICENSE file that
jaroslav@1646
    15
 * accompanied this code).
jaroslav@1646
    16
 *
jaroslav@1646
    17
 * You should have received a copy of the GNU General Public License version
jaroslav@1646
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
jaroslav@1646
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jaroslav@1646
    20
 *
jaroslav@1646
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jaroslav@1646
    22
 * or visit www.oracle.com if you need additional information or have any
jaroslav@1646
    23
 * questions.
jaroslav@1646
    24
 */
jaroslav@1646
    25
jaroslav@1646
    26
package java.lang.invoke;
jaroslav@1646
    27
jaroslav@1646
    28
import sun.invoke.util.Wrapper;
jaroslav@1646
    29
import static java.lang.invoke.MethodHandleStatics.*;
jaroslav@1646
    30
import static java.lang.invoke.MethodHandleNatives.Constants.*;
jaroslav@1646
    31
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
jaroslav@1646
    32
jaroslav@1646
    33
/**
jaroslav@1646
    34
 * Shared information for a group of method types, which differ
jaroslav@1646
    35
 * only by reference types, and therefore share a common erasure
jaroslav@1646
    36
 * and wrapping.
jaroslav@1646
    37
 * <p>
jaroslav@1646
    38
 * For an empirical discussion of the structure of method types,
jaroslav@1646
    39
 * see <a href="http://groups.google.com/group/jvm-languages/browse_thread/thread/ac9308ae74da9b7e/">
jaroslav@1646
    40
 * the thread "Avoiding Boxing" on jvm-languages</a>.
jaroslav@1646
    41
 * There are approximately 2000 distinct erased method types in the JDK.
jaroslav@1646
    42
 * There are a little over 10 times that number of unerased types.
jaroslav@1646
    43
 * No more than half of these are likely to be loaded at once.
jaroslav@1646
    44
 * @author John Rose
jaroslav@1646
    45
 */
jaroslav@1646
    46
final class MethodTypeForm {
jaroslav@1646
    47
    final int[] argToSlotTable, slotToArgTable;
jaroslav@1646
    48
    final long argCounts;               // packed slot & value counts
jaroslav@1646
    49
    final long primCounts;              // packed prim & double counts
jaroslav@1646
    50
    final int vmslots;                  // total number of parameter slots
jaroslav@1646
    51
    final MethodType erasedType;        // the canonical erasure
jaroslav@1646
    52
    final MethodType basicType;         // the canonical erasure, with primitives simplified
jaroslav@1646
    53
jaroslav@1646
    54
    // Cached adapter information:
jaroslav@1646
    55
    @Stable String typeString;           // argument type signature characters
jaroslav@1646
    56
    @Stable MethodHandle genericInvoker; // JVM hook for inexact invoke
jaroslav@1646
    57
    @Stable MethodHandle basicInvoker;   // cached instance of MH.invokeBasic
jaroslav@1646
    58
    @Stable MethodHandle namedFunctionInvoker; // cached helper for LF.NamedFunction
jaroslav@1646
    59
jaroslav@1646
    60
    // Cached lambda form information, for basic types only:
jaroslav@1646
    61
    final @Stable LambdaForm[] lambdaForms;
jaroslav@1646
    62
    // Indexes into lambdaForms:
jaroslav@1646
    63
    static final int
jaroslav@1646
    64
            LF_INVVIRTUAL     =  0,  // DMH invokeVirtual
jaroslav@1646
    65
            LF_INVSTATIC      =  1,
jaroslav@1646
    66
            LF_INVSPECIAL     =  2,
jaroslav@1646
    67
            LF_NEWINVSPECIAL  =  3,
jaroslav@1646
    68
            LF_INVINTERFACE   =  4,
jaroslav@1646
    69
            LF_INVSTATIC_INIT =  5,  // DMH invokeStatic with <clinit> barrier
jaroslav@1646
    70
            LF_INTERPRET      =  6,  // LF interpreter
jaroslav@1646
    71
            LF_COUNTER        =  7,  // CMH wrapper
jaroslav@1646
    72
            LF_REINVOKE       =  8,  // other wrapper
jaroslav@1646
    73
            LF_EX_LINKER      =  9,  // invokeExact_MT
jaroslav@1646
    74
            LF_EX_INVOKER     = 10,  // invokeExact MH
jaroslav@1646
    75
            LF_GEN_LINKER     = 11,
jaroslav@1646
    76
            LF_GEN_INVOKER    = 12,
jaroslav@1646
    77
            LF_CS_LINKER      = 13,  // linkToCallSite_CS
jaroslav@1646
    78
            LF_MH_LINKER      = 14,  // linkToCallSite_MH
jaroslav@1646
    79
            LF_LIMIT          = 15;
jaroslav@1646
    80
jaroslav@1646
    81
    public MethodType erasedType() {
jaroslav@1646
    82
        return erasedType;
jaroslav@1646
    83
    }
jaroslav@1646
    84
jaroslav@1646
    85
    public MethodType basicType() {
jaroslav@1646
    86
        return basicType;
jaroslav@1646
    87
    }
jaroslav@1646
    88
jaroslav@1646
    89
    public LambdaForm cachedLambdaForm(int which) {
jaroslav@1646
    90
        return lambdaForms[which];
jaroslav@1646
    91
    }
jaroslav@1646
    92
jaroslav@1646
    93
    public LambdaForm setCachedLambdaForm(int which, LambdaForm form) {
jaroslav@1646
    94
        // Should we perform some sort of CAS, to avoid racy duplication?
jaroslav@1646
    95
        return lambdaForms[which] = form;
jaroslav@1646
    96
    }
jaroslav@1646
    97
jaroslav@1646
    98
    public MethodHandle basicInvoker() {
jaroslav@1646
    99
        assert(erasedType == basicType) : "erasedType: " + erasedType + " != basicType: " + basicType;  // primitives must be flattened also
jaroslav@1646
   100
        MethodHandle invoker = basicInvoker;
jaroslav@1646
   101
        if (invoker != null)  return invoker;
jaroslav@1646
   102
        invoker = DirectMethodHandle.make(invokeBasicMethod(basicType));
jaroslav@1646
   103
        basicInvoker = invoker;
jaroslav@1646
   104
        return invoker;
jaroslav@1646
   105
    }
jaroslav@1646
   106
jaroslav@1646
   107
    // This next one is called from LambdaForm.NamedFunction.<init>.
jaroslav@1646
   108
    /*non-public*/ static MemberName invokeBasicMethod(MethodType basicType) {
jaroslav@1646
   109
        assert(basicType == basicType.basicType());
jaroslav@1646
   110
        try {
jaroslav@1646
   111
            // Do approximately the same as this public API call:
jaroslav@1646
   112
            //   Lookup.findVirtual(MethodHandle.class, name, type);
jaroslav@1646
   113
            // But bypass access and corner case checks, since we know exactly what we need.
jaroslav@1646
   114
            return IMPL_LOOKUP.resolveOrFail(REF_invokeVirtual, MethodHandle.class, "invokeBasic", basicType);
jaroslav@1646
   115
         } catch (ReflectiveOperationException ex) {
jaroslav@1646
   116
            throw newInternalError("JVM cannot find invoker for "+basicType, ex);
jaroslav@1646
   117
        }
jaroslav@1646
   118
    }
jaroslav@1646
   119
jaroslav@1646
   120
    /**
jaroslav@1646
   121
     * Build an MTF for a given type, which must have all references erased to Object.
jaroslav@1646
   122
     * This MTF will stand for that type and all un-erased variations.
jaroslav@1646
   123
     * Eagerly compute some basic properties of the type, common to all variations.
jaroslav@1646
   124
     */
jaroslav@1646
   125
    protected MethodTypeForm(MethodType erasedType) {
jaroslav@1646
   126
        this.erasedType = erasedType;
jaroslav@1646
   127
jaroslav@1646
   128
        Class<?>[] ptypes = erasedType.ptypes();
jaroslav@1646
   129
        int ptypeCount = ptypes.length;
jaroslav@1646
   130
        int pslotCount = ptypeCount;            // temp. estimate
jaroslav@1646
   131
        int rtypeCount = 1;                     // temp. estimate
jaroslav@1646
   132
        int rslotCount = 1;                     // temp. estimate
jaroslav@1646
   133
jaroslav@1646
   134
        int[] argToSlotTab = null, slotToArgTab = null;
jaroslav@1646
   135
jaroslav@1646
   136
        // Walk the argument types, looking for primitives.
jaroslav@1646
   137
        int pac = 0, lac = 0, prc = 0, lrc = 0;
jaroslav@1646
   138
        Class<?>[] epts = ptypes;
jaroslav@1646
   139
        Class<?>[] bpts = epts;
jaroslav@1646
   140
        for (int i = 0; i < epts.length; i++) {
jaroslav@1646
   141
            Class<?> pt = epts[i];
jaroslav@1646
   142
            if (pt != Object.class) {
jaroslav@1646
   143
                ++pac;
jaroslav@1646
   144
                Wrapper w = Wrapper.forPrimitiveType(pt);
jaroslav@1646
   145
                if (w.isDoubleWord())  ++lac;
jaroslav@1646
   146
                if (w.isSubwordOrInt() && pt != int.class) {
jaroslav@1646
   147
                    if (bpts == epts)
jaroslav@1646
   148
                        bpts = bpts.clone();
jaroslav@1646
   149
                    bpts[i] = int.class;
jaroslav@1646
   150
                }
jaroslav@1646
   151
            }
jaroslav@1646
   152
        }
jaroslav@1646
   153
        pslotCount += lac;                  // #slots = #args + #longs
jaroslav@1646
   154
        Class<?> rt = erasedType.returnType();
jaroslav@1646
   155
        Class<?> bt = rt;
jaroslav@1646
   156
        if (rt != Object.class) {
jaroslav@1646
   157
            ++prc;          // even void.class counts as a prim here
jaroslav@1646
   158
            Wrapper w = Wrapper.forPrimitiveType(rt);
jaroslav@1646
   159
            if (w.isDoubleWord())  ++lrc;
jaroslav@1646
   160
            if (w.isSubwordOrInt() && rt != int.class)
jaroslav@1646
   161
                bt = int.class;
jaroslav@1646
   162
            // adjust #slots, #args
jaroslav@1646
   163
            if (rt == void.class)
jaroslav@1646
   164
                rtypeCount = rslotCount = 0;
jaroslav@1646
   165
            else
jaroslav@1646
   166
                rslotCount += lrc;
jaroslav@1646
   167
        }
jaroslav@1646
   168
        if (epts == bpts && bt == rt) {
jaroslav@1646
   169
            this.basicType = erasedType;
jaroslav@1646
   170
        } else {
jaroslav@1646
   171
            this.basicType = MethodType.makeImpl(bt, bpts, true);
jaroslav@1646
   172
        }
jaroslav@1646
   173
        if (lac != 0) {
jaroslav@1646
   174
            int slot = ptypeCount + lac;
jaroslav@1646
   175
            slotToArgTab = new int[slot+1];
jaroslav@1646
   176
            argToSlotTab = new int[1+ptypeCount];
jaroslav@1646
   177
            argToSlotTab[0] = slot;  // argument "-1" is past end of slots
jaroslav@1646
   178
            for (int i = 0; i < epts.length; i++) {
jaroslav@1646
   179
                Class<?> pt = epts[i];
jaroslav@1646
   180
                Wrapper w = Wrapper.forBasicType(pt);
jaroslav@1646
   181
                if (w.isDoubleWord())  --slot;
jaroslav@1646
   182
                --slot;
jaroslav@1646
   183
                slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
jaroslav@1646
   184
                argToSlotTab[1+i]  = slot;
jaroslav@1646
   185
            }
jaroslav@1646
   186
            assert(slot == 0);  // filled the table
jaroslav@1646
   187
        }
jaroslav@1646
   188
        this.primCounts = pack(lrc, prc, lac, pac);
jaroslav@1646
   189
        this.argCounts = pack(rslotCount, rtypeCount, pslotCount, ptypeCount);
jaroslav@1646
   190
        if (slotToArgTab == null) {
jaroslav@1646
   191
            int slot = ptypeCount; // first arg is deepest in stack
jaroslav@1646
   192
            slotToArgTab = new int[slot+1];
jaroslav@1646
   193
            argToSlotTab = new int[1+ptypeCount];
jaroslav@1646
   194
            argToSlotTab[0] = slot;  // argument "-1" is past end of slots
jaroslav@1646
   195
            for (int i = 0; i < ptypeCount; i++) {
jaroslav@1646
   196
                --slot;
jaroslav@1646
   197
                slotToArgTab[slot] = i+1; // "+1" see argSlotToParameter note
jaroslav@1646
   198
                argToSlotTab[1+i]  = slot;
jaroslav@1646
   199
            }
jaroslav@1646
   200
        }
jaroslav@1646
   201
        this.argToSlotTable = argToSlotTab;
jaroslav@1646
   202
        this.slotToArgTable = slotToArgTab;
jaroslav@1646
   203
jaroslav@1646
   204
        if (pslotCount >= 256)  throw newIllegalArgumentException("too many arguments");
jaroslav@1646
   205
jaroslav@1646
   206
        // send a few bits down to the JVM:
jaroslav@1646
   207
        this.vmslots = parameterSlotCount();
jaroslav@1646
   208
jaroslav@1646
   209
        if (basicType == erasedType) {
jaroslav@1646
   210
            lambdaForms = new LambdaForm[LF_LIMIT];
jaroslav@1646
   211
        } else {
jaroslav@1646
   212
            lambdaForms = null;  // could be basicType.form().lambdaForms;
jaroslav@1646
   213
        }
jaroslav@1646
   214
    }
jaroslav@1646
   215
jaroslav@1646
   216
    private static long pack(int a, int b, int c, int d) {
jaroslav@1646
   217
        assert(((a|b|c|d) & ~0xFFFF) == 0);
jaroslav@1646
   218
        long hw = ((a << 16) | b), lw = ((c << 16) | d);
jaroslav@1646
   219
        return (hw << 32) | lw;
jaroslav@1646
   220
    }
jaroslav@1646
   221
    private static char unpack(long packed, int word) { // word==0 => return a, ==3 => return d
jaroslav@1646
   222
        assert(word <= 3);
jaroslav@1646
   223
        return (char)(packed >> ((3-word) * 16));
jaroslav@1646
   224
    }
jaroslav@1646
   225
jaroslav@1646
   226
    public int parameterCount() {                      // # outgoing values
jaroslav@1646
   227
        return unpack(argCounts, 3);
jaroslav@1646
   228
    }
jaroslav@1646
   229
    public int parameterSlotCount() {                  // # outgoing interpreter slots
jaroslav@1646
   230
        return unpack(argCounts, 2);
jaroslav@1646
   231
    }
jaroslav@1646
   232
    public int returnCount() {                         // = 0 (V), or 1
jaroslav@1646
   233
        return unpack(argCounts, 1);
jaroslav@1646
   234
    }
jaroslav@1646
   235
    public int returnSlotCount() {                     // = 0 (V), 2 (J/D), or 1
jaroslav@1646
   236
        return unpack(argCounts, 0);
jaroslav@1646
   237
    }
jaroslav@1646
   238
    public int primitiveParameterCount() {
jaroslav@1646
   239
        return unpack(primCounts, 3);
jaroslav@1646
   240
    }
jaroslav@1646
   241
    public int longPrimitiveParameterCount() {
jaroslav@1646
   242
        return unpack(primCounts, 2);
jaroslav@1646
   243
    }
jaroslav@1646
   244
    public int primitiveReturnCount() {                // = 0 (obj), or 1
jaroslav@1646
   245
        return unpack(primCounts, 1);
jaroslav@1646
   246
    }
jaroslav@1646
   247
    public int longPrimitiveReturnCount() {            // = 1 (J/D), or 0
jaroslav@1646
   248
        return unpack(primCounts, 0);
jaroslav@1646
   249
    }
jaroslav@1646
   250
    public boolean hasPrimitives() {
jaroslav@1646
   251
        return primCounts != 0;
jaroslav@1646
   252
    }
jaroslav@1646
   253
    public boolean hasNonVoidPrimitives() {
jaroslav@1646
   254
        if (primCounts == 0)  return false;
jaroslav@1646
   255
        if (primitiveParameterCount() != 0)  return true;
jaroslav@1646
   256
        return (primitiveReturnCount() != 0 && returnCount() != 0);
jaroslav@1646
   257
    }
jaroslav@1646
   258
    public boolean hasLongPrimitives() {
jaroslav@1646
   259
        return (longPrimitiveParameterCount() | longPrimitiveReturnCount()) != 0;
jaroslav@1646
   260
    }
jaroslav@1646
   261
    public int parameterToArgSlot(int i) {
jaroslav@1646
   262
        return argToSlotTable[1+i];
jaroslav@1646
   263
    }
jaroslav@1646
   264
    public int argSlotToParameter(int argSlot) {
jaroslav@1646
   265
        // Note:  Empty slots are represented by zero in this table.
jaroslav@1646
   266
        // Valid arguments slots contain incremented entries, so as to be non-zero.
jaroslav@1646
   267
        // We return -1 the caller to mean an empty slot.
jaroslav@1646
   268
        return slotToArgTable[argSlot] - 1;
jaroslav@1646
   269
    }
jaroslav@1646
   270
jaroslav@1646
   271
    static MethodTypeForm findForm(MethodType mt) {
jaroslav@1646
   272
        MethodType erased = canonicalize(mt, ERASE, ERASE);
jaroslav@1646
   273
        if (erased == null) {
jaroslav@1646
   274
            // It is already erased.  Make a new MethodTypeForm.
jaroslav@1646
   275
            return new MethodTypeForm(mt);
jaroslav@1646
   276
        } else {
jaroslav@1646
   277
            // Share the MethodTypeForm with the erased version.
jaroslav@1646
   278
            return erased.form();
jaroslav@1646
   279
        }
jaroslav@1646
   280
    }
jaroslav@1646
   281
jaroslav@1646
   282
    /** Codes for {@link #canonicalize(java.lang.Class, int)}.
jaroslav@1646
   283
     * ERASE means change every reference to {@code Object}.
jaroslav@1646
   284
     * WRAP means convert primitives (including {@code void} to their
jaroslav@1646
   285
     * corresponding wrapper types.  UNWRAP means the reverse of WRAP.
jaroslav@1646
   286
     * INTS means convert all non-void primitive types to int or long,
jaroslav@1646
   287
     * according to size.  LONGS means convert all non-void primitives
jaroslav@1646
   288
     * to long, regardless of size.  RAW_RETURN means convert a type
jaroslav@1646
   289
     * (assumed to be a return type) to int if it is smaller than an int,
jaroslav@1646
   290
     * or if it is void.
jaroslav@1646
   291
     */
jaroslav@1646
   292
    public static final int NO_CHANGE = 0, ERASE = 1, WRAP = 2, UNWRAP = 3, INTS = 4, LONGS = 5, RAW_RETURN = 6;
jaroslav@1646
   293
jaroslav@1646
   294
    /** Canonicalize the types in the given method type.
jaroslav@1646
   295
     * If any types change, intern the new type, and return it.
jaroslav@1646
   296
     * Otherwise return null.
jaroslav@1646
   297
     */
jaroslav@1646
   298
    public static MethodType canonicalize(MethodType mt, int howRet, int howArgs) {
jaroslav@1646
   299
        Class<?>[] ptypes = mt.ptypes();
jaroslav@1646
   300
        Class<?>[] ptc = MethodTypeForm.canonicalizes(ptypes, howArgs);
jaroslav@1646
   301
        Class<?> rtype = mt.returnType();
jaroslav@1646
   302
        Class<?> rtc = MethodTypeForm.canonicalize(rtype, howRet);
jaroslav@1646
   303
        if (ptc == null && rtc == null) {
jaroslav@1646
   304
            // It is already canonical.
jaroslav@1646
   305
            return null;
jaroslav@1646
   306
        }
jaroslav@1646
   307
        // Find the erased version of the method type:
jaroslav@1646
   308
        if (rtc == null)  rtc = rtype;
jaroslav@1646
   309
        if (ptc == null)  ptc = ptypes;
jaroslav@1646
   310
        return MethodType.makeImpl(rtc, ptc, true);
jaroslav@1646
   311
    }
jaroslav@1646
   312
jaroslav@1646
   313
    /** Canonicalize the given return or param type.
jaroslav@1646
   314
     *  Return null if the type is already canonicalized.
jaroslav@1646
   315
     */
jaroslav@1646
   316
    static Class<?> canonicalize(Class<?> t, int how) {
jaroslav@1646
   317
        Class<?> ct;
jaroslav@1646
   318
        if (t == Object.class) {
jaroslav@1646
   319
            // no change, ever
jaroslav@1646
   320
        } else if (!t.isPrimitive()) {
jaroslav@1646
   321
            switch (how) {
jaroslav@1646
   322
                case UNWRAP:
jaroslav@1646
   323
                    ct = Wrapper.asPrimitiveType(t);
jaroslav@1646
   324
                    if (ct != t)  return ct;
jaroslav@1646
   325
                    break;
jaroslav@1646
   326
                case RAW_RETURN:
jaroslav@1646
   327
                case ERASE:
jaroslav@1646
   328
                    return Object.class;
jaroslav@1646
   329
            }
jaroslav@1646
   330
        } else if (t == void.class) {
jaroslav@1646
   331
            // no change, usually
jaroslav@1646
   332
            switch (how) {
jaroslav@1646
   333
                case RAW_RETURN:
jaroslav@1646
   334
                    return int.class;
jaroslav@1646
   335
                case WRAP:
jaroslav@1646
   336
                    return Void.class;
jaroslav@1646
   337
            }
jaroslav@1646
   338
        } else {
jaroslav@1646
   339
            // non-void primitive
jaroslav@1646
   340
            switch (how) {
jaroslav@1646
   341
                case WRAP:
jaroslav@1646
   342
                    return Wrapper.asWrapperType(t);
jaroslav@1646
   343
                case INTS:
jaroslav@1646
   344
                    if (t == int.class || t == long.class)
jaroslav@1646
   345
                        return null;  // no change
jaroslav@1646
   346
                    if (t == double.class)
jaroslav@1646
   347
                        return long.class;
jaroslav@1646
   348
                    return int.class;
jaroslav@1646
   349
                case LONGS:
jaroslav@1646
   350
                    if (t == long.class)
jaroslav@1646
   351
                        return null;  // no change
jaroslav@1646
   352
                    return long.class;
jaroslav@1646
   353
                case RAW_RETURN:
jaroslav@1646
   354
                    if (t == int.class || t == long.class ||
jaroslav@1646
   355
                        t == float.class || t == double.class)
jaroslav@1646
   356
                        return null;  // no change
jaroslav@1646
   357
                    // everything else returns as an int
jaroslav@1646
   358
                    return int.class;
jaroslav@1646
   359
            }
jaroslav@1646
   360
        }
jaroslav@1646
   361
        // no change; return null to signify
jaroslav@1646
   362
        return null;
jaroslav@1646
   363
    }
jaroslav@1646
   364
jaroslav@1646
   365
    /** Canonicalize each param type in the given array.
jaroslav@1646
   366
     *  Return null if all types are already canonicalized.
jaroslav@1646
   367
     */
jaroslav@1646
   368
    static Class<?>[] canonicalizes(Class<?>[] ts, int how) {
jaroslav@1646
   369
        Class<?>[] cs = null;
jaroslav@1646
   370
        for (int imax = ts.length, i = 0; i < imax; i++) {
jaroslav@1646
   371
            Class<?> c = canonicalize(ts[i], how);
jaroslav@1646
   372
            if (c == void.class)
jaroslav@1646
   373
                c = null;  // a Void parameter was unwrapped to void; ignore
jaroslav@1646
   374
            if (c != null) {
jaroslav@1646
   375
                if (cs == null)
jaroslav@1646
   376
                    cs = ts.clone();
jaroslav@1646
   377
                cs[i] = c;
jaroslav@1646
   378
            }
jaroslav@1646
   379
        }
jaroslav@1646
   380
        return cs;
jaroslav@1646
   381
    }
jaroslav@1646
   382
jaroslav@1646
   383
    @Override
jaroslav@1646
   384
    public String toString() {
jaroslav@1646
   385
        return "Form"+erasedType;
jaroslav@1646
   386
    }
jaroslav@1646
   387
jaroslav@1646
   388
}