1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/rt/emul/compact/src/main/java/java/lang/invoke/BoundMethodHandle.java Sat Aug 09 11:11:13 2014 +0200
1.3 @@ -0,0 +1,864 @@
1.4 +/*
1.5 + * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
1.7 + *
1.8 + * This code is free software; you can redistribute it and/or modify it
1.9 + * under the terms of the GNU General Public License version 2 only, as
1.10 + * published by the Free Software Foundation. Oracle designates this
1.11 + * particular file as subject to the "Classpath" exception as provided
1.12 + * by Oracle in the LICENSE file that accompanied this code.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1.17 + * version 2 for more details (a copy is included in the LICENSE file that
1.18 + * accompanied this code).
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License version
1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1.23 + *
1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1.25 + * or visit www.oracle.com if you need additional information or have any
1.26 + * questions.
1.27 + */
1.28 +
1.29 +package java.lang.invoke;
1.30 +
1.31 +import static jdk.internal.org.objectweb.asm.Opcodes.*;
1.32 +import static java.lang.invoke.LambdaForm.basicTypes;
1.33 +import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
1.34 +import static java.lang.invoke.MethodHandleStatics.*;
1.35 +
1.36 +import java.lang.invoke.LambdaForm.Name;
1.37 +import java.lang.invoke.LambdaForm.NamedFunction;
1.38 +import java.lang.invoke.MethodHandles.Lookup;
1.39 +import java.lang.reflect.Field;
1.40 +import java.util.Arrays;
1.41 +import java.util.HashMap;
1.42 +
1.43 +import sun.invoke.util.ValueConversions;
1.44 +import sun.invoke.util.Wrapper;
1.45 +
1.46 +import jdk.internal.org.objectweb.asm.ClassWriter;
1.47 +import jdk.internal.org.objectweb.asm.MethodVisitor;
1.48 +import jdk.internal.org.objectweb.asm.Type;
1.49 +
1.50 +/**
1.51 + * The flavor of method handle which emulates an invoke instruction
1.52 + * on a predetermined argument. The JVM dispatches to the correct method
1.53 + * when the handle is created, not when it is invoked.
1.54 + *
1.55 + * All bound arguments are encapsulated in dedicated species.
1.56 + */
1.57 +/* non-public */ abstract class BoundMethodHandle extends MethodHandle {
1.58 +
1.59 + /* non-public */ BoundMethodHandle(MethodType type, LambdaForm form) {
1.60 + super(type, form);
1.61 + }
1.62 +
1.63 + //
1.64 + // BMH API and internals
1.65 + //
1.66 +
1.67 + static MethodHandle bindSingle(MethodType type, LambdaForm form, char xtype, Object x) {
1.68 + // for some type signatures, there exist pre-defined concrete BMH classes
1.69 + try {
1.70 + switch (xtype) {
1.71 + case 'L':
1.72 + if (true) return bindSingle(type, form, x); // Use known fast path.
1.73 + return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('L').constructor[0].invokeBasic(type, form, x);
1.74 + case 'I':
1.75 + return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('I').constructor[0].invokeBasic(type, form, ValueConversions.widenSubword(x));
1.76 + case 'J':
1.77 + return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('J').constructor[0].invokeBasic(type, form, (long) x);
1.78 + case 'F':
1.79 + return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('F').constructor[0].invokeBasic(type, form, (float) x);
1.80 + case 'D':
1.81 + return (BoundMethodHandle) SpeciesData.EMPTY.extendWithType('D').constructor[0].invokeBasic(type, form, (double) x);
1.82 + default : throw new InternalError("unexpected xtype: " + xtype);
1.83 + }
1.84 + } catch (Throwable t) {
1.85 + throw newInternalError(t);
1.86 + }
1.87 + }
1.88 +
1.89 + static MethodHandle bindSingle(MethodType type, LambdaForm form, Object x) {
1.90 + return new Species_L(type, form, x);
1.91 + }
1.92 +
1.93 + MethodHandle cloneExtend(MethodType type, LambdaForm form, char xtype, Object x) {
1.94 + try {
1.95 + switch (xtype) {
1.96 + case 'L': return cloneExtendL(type, form, x);
1.97 + case 'I': return cloneExtendI(type, form, ValueConversions.widenSubword(x));
1.98 + case 'J': return cloneExtendJ(type, form, (long) x);
1.99 + case 'F': return cloneExtendF(type, form, (float) x);
1.100 + case 'D': return cloneExtendD(type, form, (double) x);
1.101 + }
1.102 + } catch (Throwable t) {
1.103 + throw newInternalError(t);
1.104 + }
1.105 + throw new InternalError("unexpected type: " + xtype);
1.106 + }
1.107 +
1.108 + @Override
1.109 + MethodHandle bindArgument(int pos, char basicType, Object value) {
1.110 + MethodType type = type().dropParameterTypes(pos, pos+1);
1.111 + LambdaForm form = internalForm().bind(1+pos, speciesData());
1.112 + return cloneExtend(type, form, basicType, value);
1.113 + }
1.114 +
1.115 + @Override
1.116 + MethodHandle dropArguments(MethodType srcType, int pos, int drops) {
1.117 + LambdaForm form = internalForm().addArguments(pos, srcType.parameterList().subList(pos, pos+drops));
1.118 + try {
1.119 + return clone(srcType, form);
1.120 + } catch (Throwable t) {
1.121 + throw newInternalError(t);
1.122 + }
1.123 + }
1.124 +
1.125 + @Override
1.126 + MethodHandle permuteArguments(MethodType newType, int[] reorder) {
1.127 + try {
1.128 + return clone(newType, form.permuteArguments(1, reorder, basicTypes(newType.parameterList())));
1.129 + } catch (Throwable t) {
1.130 + throw newInternalError(t);
1.131 + }
1.132 + }
1.133 +
1.134 + static final String EXTENSION_TYPES = "LIJFD";
1.135 + static final byte INDEX_L = 0, INDEX_I = 1, INDEX_J = 2, INDEX_F = 3, INDEX_D = 4;
1.136 + static byte extensionIndex(char type) {
1.137 + int i = EXTENSION_TYPES.indexOf(type);
1.138 + if (i < 0) throw new InternalError();
1.139 + return (byte) i;
1.140 + }
1.141 +
1.142 + /**
1.143 + * Return the {@link SpeciesData} instance representing this BMH species. All subclasses must provide a
1.144 + * static field containing this value, and they must accordingly implement this method.
1.145 + */
1.146 + protected abstract SpeciesData speciesData();
1.147 +
1.148 + @Override
1.149 + final Object internalProperties() {
1.150 + return "/BMH="+internalValues();
1.151 + }
1.152 +
1.153 + @Override
1.154 + final Object internalValues() {
1.155 + Object[] boundValues = new Object[speciesData().fieldCount()];
1.156 + for (int i = 0; i < boundValues.length; ++i) {
1.157 + boundValues[i] = arg(i);
1.158 + }
1.159 + return Arrays.asList(boundValues);
1.160 + }
1.161 +
1.162 + public final Object arg(int i) {
1.163 + try {
1.164 + switch (speciesData().fieldType(i)) {
1.165 + case 'L': return argL(i);
1.166 + case 'I': return argI(i);
1.167 + case 'F': return argF(i);
1.168 + case 'D': return argD(i);
1.169 + case 'J': return argJ(i);
1.170 + }
1.171 + } catch (Throwable ex) {
1.172 + throw newInternalError(ex);
1.173 + }
1.174 + throw new InternalError("unexpected type: " + speciesData().types+"."+i);
1.175 + }
1.176 + public final Object argL(int i) throws Throwable { return speciesData().getters[i].invokeBasic(this); }
1.177 + public final int argI(int i) throws Throwable { return (int) speciesData().getters[i].invokeBasic(this); }
1.178 + public final float argF(int i) throws Throwable { return (float) speciesData().getters[i].invokeBasic(this); }
1.179 + public final double argD(int i) throws Throwable { return (double) speciesData().getters[i].invokeBasic(this); }
1.180 + public final long argJ(int i) throws Throwable { return (long) speciesData().getters[i].invokeBasic(this); }
1.181 +
1.182 + //
1.183 + // cloning API
1.184 + //
1.185 +
1.186 + public abstract BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable;
1.187 + public abstract BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable;
1.188 + public abstract BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable;
1.189 + public abstract BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable;
1.190 + public abstract BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable;
1.191 + public abstract BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable;
1.192 +
1.193 + // The following is a grossly irregular hack:
1.194 + @Override MethodHandle reinvokerTarget() {
1.195 + try {
1.196 + return (MethodHandle) argL(0);
1.197 + } catch (Throwable ex) {
1.198 + throw newInternalError(ex);
1.199 + }
1.200 + }
1.201 +
1.202 + //
1.203 + // concrete BMH classes required to close bootstrap loops
1.204 + //
1.205 +
1.206 + private // make it private to force users to access the enclosing class first
1.207 + static final class Species_L extends BoundMethodHandle {
1.208 + final Object argL0;
1.209 + public Species_L(MethodType mt, LambdaForm lf, Object argL0) {
1.210 + super(mt, lf);
1.211 + this.argL0 = argL0;
1.212 + }
1.213 + // The following is a grossly irregular hack:
1.214 + @Override MethodHandle reinvokerTarget() { return (MethodHandle) argL0; }
1.215 + @Override
1.216 + public SpeciesData speciesData() {
1.217 + return SPECIES_DATA;
1.218 + }
1.219 + public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("L", Species_L.class);
1.220 + @Override
1.221 + public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
1.222 + return new Species_L(mt, lf, argL0);
1.223 + }
1.224 + @Override
1.225 + public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
1.226 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, narg);
1.227 + }
1.228 + @Override
1.229 + public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
1.230 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, narg);
1.231 + }
1.232 + @Override
1.233 + public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
1.234 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, narg);
1.235 + }
1.236 + @Override
1.237 + public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
1.238 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, narg);
1.239 + }
1.240 + @Override
1.241 + public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
1.242 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, narg);
1.243 + }
1.244 + }
1.245 +
1.246 +/*
1.247 + static final class Species_LL extends BoundMethodHandle {
1.248 + final Object argL0;
1.249 + final Object argL1;
1.250 + public Species_LL(MethodType mt, LambdaForm lf, Object argL0, Object argL1) {
1.251 + super(mt, lf);
1.252 + this.argL0 = argL0;
1.253 + this.argL1 = argL1;
1.254 + }
1.255 + @Override
1.256 + public SpeciesData speciesData() {
1.257 + return SPECIES_DATA;
1.258 + }
1.259 + public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LL", Species_LL.class);
1.260 + @Override
1.261 + public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
1.262 + return new Species_LL(mt, lf, argL0, argL1);
1.263 + }
1.264 + @Override
1.265 + public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
1.266 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
1.267 + }
1.268 + @Override
1.269 + public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
1.270 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
1.271 + }
1.272 + @Override
1.273 + public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
1.274 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
1.275 + }
1.276 + @Override
1.277 + public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
1.278 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
1.279 + }
1.280 + @Override
1.281 + public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
1.282 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, narg);
1.283 + }
1.284 + }
1.285 +
1.286 + static final class Species_JL extends BoundMethodHandle {
1.287 + final long argJ0;
1.288 + final Object argL1;
1.289 + public Species_JL(MethodType mt, LambdaForm lf, long argJ0, Object argL1) {
1.290 + super(mt, lf);
1.291 + this.argJ0 = argJ0;
1.292 + this.argL1 = argL1;
1.293 + }
1.294 + @Override
1.295 + public SpeciesData speciesData() {
1.296 + return SPECIES_DATA;
1.297 + }
1.298 + public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("JL", Species_JL.class);
1.299 + @Override public final long argJ0() { return argJ0; }
1.300 + @Override public final Object argL1() { return argL1; }
1.301 + @Override
1.302 + public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) throws Throwable {
1.303 + return new Species_JL(mt, lf, argJ0, argL1);
1.304 + }
1.305 + @Override
1.306 + public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) throws Throwable {
1.307 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
1.308 + }
1.309 + @Override
1.310 + public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) throws Throwable {
1.311 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
1.312 + }
1.313 + @Override
1.314 + public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) throws Throwable {
1.315 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
1.316 + }
1.317 + @Override
1.318 + public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) throws Throwable {
1.319 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
1.320 + }
1.321 + @Override
1.322 + public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) throws Throwable {
1.323 + return (BoundMethodHandle) SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argJ0, argL1, narg);
1.324 + }
1.325 + }
1.326 +*/
1.327 +
1.328 + //
1.329 + // BMH species meta-data
1.330 + //
1.331 +
1.332 + /**
1.333 + * Meta-data wrapper for concrete BMH classes.
1.334 + */
1.335 + static class SpeciesData {
1.336 + final String types;
1.337 + final Class<? extends BoundMethodHandle> clazz;
1.338 + // Bootstrapping requires circular relations MH -> BMH -> SpeciesData -> MH
1.339 + // Therefore, we need a non-final link in the chain. Use array elements.
1.340 + final MethodHandle[] constructor;
1.341 + final MethodHandle[] getters;
1.342 + final SpeciesData[] extensions;
1.343 +
1.344 + public int fieldCount() {
1.345 + return types.length();
1.346 + }
1.347 + public char fieldType(int i) {
1.348 + return types.charAt(i);
1.349 + }
1.350 +
1.351 + public String toString() {
1.352 + return "SpeciesData["+(isPlaceholder() ? "<placeholder>" : clazz.getSimpleName())+":"+types+"]";
1.353 + }
1.354 +
1.355 + /**
1.356 + * Return a {@link LambdaForm.Name} containing a {@link LambdaForm.NamedFunction} that
1.357 + * represents a MH bound to a generic invoker, which in turn forwards to the corresponding
1.358 + * getter.
1.359 + */
1.360 + Name getterName(Name mhName, int i) {
1.361 + MethodHandle mh = getters[i];
1.362 + assert(mh != null) : this+"."+i;
1.363 + return new Name(mh, mhName);
1.364 + }
1.365 +
1.366 + NamedFunction getterFunction(int i) {
1.367 + return new NamedFunction(getters[i]);
1.368 + }
1.369 +
1.370 + static final SpeciesData EMPTY = new SpeciesData("", BoundMethodHandle.class);
1.371 +
1.372 + private SpeciesData(String types, Class<? extends BoundMethodHandle> clazz) {
1.373 + this.types = types;
1.374 + this.clazz = clazz;
1.375 + if (!INIT_DONE) {
1.376 + this.constructor = new MethodHandle[1];
1.377 + this.getters = new MethodHandle[types.length()];
1.378 + } else {
1.379 + this.constructor = Factory.makeCtors(clazz, types, null);
1.380 + this.getters = Factory.makeGetters(clazz, types, null);
1.381 + }
1.382 + this.extensions = new SpeciesData[EXTENSION_TYPES.length()];
1.383 + }
1.384 +
1.385 + private void initForBootstrap() {
1.386 + assert(!INIT_DONE);
1.387 + if (constructor[0] == null) {
1.388 + Factory.makeCtors(clazz, types, this.constructor);
1.389 + Factory.makeGetters(clazz, types, this.getters);
1.390 + }
1.391 + }
1.392 +
1.393 + private SpeciesData(String types) {
1.394 + // Placeholder only.
1.395 + this.types = types;
1.396 + this.clazz = null;
1.397 + this.constructor = null;
1.398 + this.getters = null;
1.399 + this.extensions = null;
1.400 + }
1.401 + private boolean isPlaceholder() { return clazz == null; }
1.402 +
1.403 + private static final HashMap<String, SpeciesData> CACHE = new HashMap<>();
1.404 + static { CACHE.put("", EMPTY); } // make bootstrap predictable
1.405 + private static final boolean INIT_DONE; // set after <clinit> finishes...
1.406 +
1.407 + SpeciesData extendWithType(char type) {
1.408 + int i = extensionIndex(type);
1.409 + SpeciesData d = extensions[i];
1.410 + if (d != null) return d;
1.411 + extensions[i] = d = get(types+type);
1.412 + return d;
1.413 + }
1.414 +
1.415 + SpeciesData extendWithIndex(byte index) {
1.416 + SpeciesData d = extensions[index];
1.417 + if (d != null) return d;
1.418 + extensions[index] = d = get(types+EXTENSION_TYPES.charAt(index));
1.419 + return d;
1.420 + }
1.421 +
1.422 + private static SpeciesData get(String types) {
1.423 + // Acquire cache lock for query.
1.424 + SpeciesData d = lookupCache(types);
1.425 + if (!d.isPlaceholder())
1.426 + return d;
1.427 + synchronized (d) {
1.428 + // Use synch. on the placeholder to prevent multiple instantiation of one species.
1.429 + // Creating this class forces a recursive call to getForClass.
1.430 + if (lookupCache(types).isPlaceholder())
1.431 + Factory.generateConcreteBMHClass(types);
1.432 + }
1.433 + // Reacquire cache lock.
1.434 + d = lookupCache(types);
1.435 + // Class loading must have upgraded the cache.
1.436 + assert(d != null && !d.isPlaceholder());
1.437 + return d;
1.438 + }
1.439 + static SpeciesData getForClass(String types, Class<? extends BoundMethodHandle> clazz) {
1.440 + // clazz is a new class which is initializing its SPECIES_DATA field
1.441 + return updateCache(types, new SpeciesData(types, clazz));
1.442 + }
1.443 + private static synchronized SpeciesData lookupCache(String types) {
1.444 + SpeciesData d = CACHE.get(types);
1.445 + if (d != null) return d;
1.446 + d = new SpeciesData(types);
1.447 + assert(d.isPlaceholder());
1.448 + CACHE.put(types, d);
1.449 + return d;
1.450 + }
1.451 + private static synchronized SpeciesData updateCache(String types, SpeciesData d) {
1.452 + SpeciesData d2;
1.453 + assert((d2 = CACHE.get(types)) == null || d2.isPlaceholder());
1.454 + assert(!d.isPlaceholder());
1.455 + CACHE.put(types, d);
1.456 + return d;
1.457 + }
1.458 +
1.459 + static {
1.460 + // pre-fill the BMH speciesdata cache with BMH's inner classes
1.461 + final Class<BoundMethodHandle> rootCls = BoundMethodHandle.class;
1.462 + SpeciesData d0 = BoundMethodHandle.SPECIES_DATA; // trigger class init
1.463 + assert(d0 == null || d0 == lookupCache("")) : d0;
1.464 + try {
1.465 + for (Class<?> c : rootCls.getDeclaredClasses()) {
1.466 + if (rootCls.isAssignableFrom(c)) {
1.467 + final Class<? extends BoundMethodHandle> cbmh = c.asSubclass(BoundMethodHandle.class);
1.468 + SpeciesData d = Factory.speciesDataFromConcreteBMHClass(cbmh);
1.469 + assert(d != null) : cbmh.getName();
1.470 + assert(d.clazz == cbmh);
1.471 + assert(d == lookupCache(d.types));
1.472 + }
1.473 + }
1.474 + } catch (Throwable e) {
1.475 + throw newInternalError(e);
1.476 + }
1.477 +
1.478 + for (SpeciesData d : CACHE.values()) {
1.479 + d.initForBootstrap();
1.480 + }
1.481 + // Note: Do not simplify this, because INIT_DONE must not be
1.482 + // a compile-time constant during bootstrapping.
1.483 + INIT_DONE = Boolean.TRUE;
1.484 + }
1.485 + }
1.486 +
1.487 + static SpeciesData getSpeciesData(String types) {
1.488 + return SpeciesData.get(types);
1.489 + }
1.490 +
1.491 + /**
1.492 + * Generation of concrete BMH classes.
1.493 + *
1.494 + * A concrete BMH species is fit for binding a number of values adhering to a
1.495 + * given type pattern. Reference types are erased.
1.496 + *
1.497 + * BMH species are cached by type pattern.
1.498 + *
1.499 + * A BMH species has a number of fields with the concrete (possibly erased) types of
1.500 + * bound values. Setters are provided as an API in BMH. Getters are exposed as MHs,
1.501 + * which can be included as names in lambda forms.
1.502 + */
1.503 + static class Factory {
1.504 +
1.505 + static final String JLO_SIG = "Ljava/lang/Object;";
1.506 + static final String JLS_SIG = "Ljava/lang/String;";
1.507 + static final String JLC_SIG = "Ljava/lang/Class;";
1.508 + static final String MH = "java/lang/invoke/MethodHandle";
1.509 + static final String MH_SIG = "L"+MH+";";
1.510 + static final String BMH = "java/lang/invoke/BoundMethodHandle";
1.511 + static final String BMH_SIG = "L"+BMH+";";
1.512 + static final String SPECIES_DATA = "java/lang/invoke/BoundMethodHandle$SpeciesData";
1.513 + static final String SPECIES_DATA_SIG = "L"+SPECIES_DATA+";";
1.514 +
1.515 + static final String SPECIES_PREFIX_NAME = "Species_";
1.516 + static final String SPECIES_PREFIX_PATH = BMH + "$" + SPECIES_PREFIX_NAME;
1.517 +
1.518 + static final String BMHSPECIES_DATA_EWI_SIG = "(B)" + SPECIES_DATA_SIG;
1.519 + static final String BMHSPECIES_DATA_GFC_SIG = "(" + JLS_SIG + JLC_SIG + ")" + SPECIES_DATA_SIG;
1.520 + static final String MYSPECIES_DATA_SIG = "()" + SPECIES_DATA_SIG;
1.521 + static final String VOID_SIG = "()V";
1.522 +
1.523 + static final String SIG_INCIPIT = "(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;";
1.524 +
1.525 + static final Class<?>[] TYPES = new Class<?>[] { Object.class, int.class, long.class, float.class, double.class };
1.526 +
1.527 + static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" };
1.528 +
1.529 + /**
1.530 + * Generate a concrete subclass of BMH for a given combination of bound types.
1.531 + *
1.532 + * A concrete BMH species adheres to the following schema:
1.533 + *
1.534 + * <pre>
1.535 + * class Species_[[types]] extends BoundMethodHandle {
1.536 + * [[fields]]
1.537 + * final SpeciesData speciesData() { return SpeciesData.get("[[types]]"); }
1.538 + * }
1.539 + * </pre>
1.540 + *
1.541 + * The {@code [[types]]} signature is precisely the string that is passed to this
1.542 + * method.
1.543 + *
1.544 + * The {@code [[fields]]} section consists of one field definition per character in
1.545 + * the type signature, adhering to the naming schema described in the definition of
1.546 + * {@link #makeFieldName}.
1.547 + *
1.548 + * For example, a concrete BMH species for two reference and one integral bound values
1.549 + * would have the following shape:
1.550 + *
1.551 + * <pre>
1.552 + * class BoundMethodHandle { ... private static
1.553 + * final class Species_LLI extends BoundMethodHandle {
1.554 + * final Object argL0;
1.555 + * final Object argL1;
1.556 + * final int argI2;
1.557 + * public Species_LLI(MethodType mt, LambdaForm lf, Object argL0, Object argL1, int argI2) {
1.558 + * super(mt, lf);
1.559 + * this.argL0 = argL0;
1.560 + * this.argL1 = argL1;
1.561 + * this.argI2 = argI2;
1.562 + * }
1.563 + * public final SpeciesData speciesData() { return SPECIES_DATA; }
1.564 + * public static final SpeciesData SPECIES_DATA = SpeciesData.getForClass("LLI", Species_LLI.class);
1.565 + * public final BoundMethodHandle clone(MethodType mt, LambdaForm lf) {
1.566 + * return SPECIES_DATA.constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2);
1.567 + * }
1.568 + * public final BoundMethodHandle cloneExtendL(MethodType mt, LambdaForm lf, Object narg) {
1.569 + * return SPECIES_DATA.extendWithIndex(INDEX_L).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
1.570 + * }
1.571 + * public final BoundMethodHandle cloneExtendI(MethodType mt, LambdaForm lf, int narg) {
1.572 + * return SPECIES_DATA.extendWithIndex(INDEX_I).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
1.573 + * }
1.574 + * public final BoundMethodHandle cloneExtendJ(MethodType mt, LambdaForm lf, long narg) {
1.575 + * return SPECIES_DATA.extendWithIndex(INDEX_J).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
1.576 + * }
1.577 + * public final BoundMethodHandle cloneExtendF(MethodType mt, LambdaForm lf, float narg) {
1.578 + * return SPECIES_DATA.extendWithIndex(INDEX_F).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
1.579 + * }
1.580 + * public final BoundMethodHandle cloneExtendD(MethodType mt, LambdaForm lf, double narg) {
1.581 + * return SPECIES_DATA.extendWithIndex(INDEX_D).constructor[0].invokeBasic(mt, lf, argL0, argL1, argI2, narg);
1.582 + * }
1.583 + * }
1.584 + * </pre>
1.585 + *
1.586 + * @param types the type signature, wherein reference types are erased to 'L'
1.587 + * @return the generated concrete BMH class
1.588 + */
1.589 + static Class<? extends BoundMethodHandle> generateConcreteBMHClass(String types) {
1.590 + final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
1.591 +
1.592 + final String className = SPECIES_PREFIX_PATH + types;
1.593 + final String sourceFile = SPECIES_PREFIX_NAME + types;
1.594 + cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, BMH, null);
1.595 + cw.visitSource(sourceFile, null);
1.596 +
1.597 + // emit static types and SPECIES_DATA fields
1.598 + cw.visitField(ACC_PUBLIC + ACC_STATIC, "SPECIES_DATA", SPECIES_DATA_SIG, null, null).visitEnd();
1.599 +
1.600 + // emit bound argument fields
1.601 + for (int i = 0; i < types.length(); ++i) {
1.602 + final char t = types.charAt(i);
1.603 + final String fieldName = makeFieldName(types, i);
1.604 + final String fieldDesc = t == 'L' ? JLO_SIG : String.valueOf(t);
1.605 + cw.visitField(ACC_FINAL, fieldName, fieldDesc, null, null).visitEnd();
1.606 + }
1.607 +
1.608 + MethodVisitor mv;
1.609 +
1.610 + // emit constructor
1.611 + mv = cw.visitMethod(ACC_PUBLIC, "<init>", makeSignature(types, true), null, null);
1.612 + mv.visitCode();
1.613 + mv.visitVarInsn(ALOAD, 0);
1.614 + mv.visitVarInsn(ALOAD, 1);
1.615 + mv.visitVarInsn(ALOAD, 2);
1.616 +
1.617 + mv.visitMethodInsn(INVOKESPECIAL, BMH, "<init>", makeSignature("", true));
1.618 +
1.619 + for (int i = 0, j = 0; i < types.length(); ++i, ++j) {
1.620 + // i counts the arguments, j counts corresponding argument slots
1.621 + char t = types.charAt(i);
1.622 + mv.visitVarInsn(ALOAD, 0);
1.623 + mv.visitVarInsn(typeLoadOp(t), j + 3); // parameters start at 3
1.624 + mv.visitFieldInsn(PUTFIELD, className, makeFieldName(types, i), typeSig(t));
1.625 + if (t == 'J' || t == 'D') {
1.626 + ++j; // adjust argument register access
1.627 + }
1.628 + }
1.629 +
1.630 + mv.visitInsn(RETURN);
1.631 + mv.visitMaxs(0, 0);
1.632 + mv.visitEnd();
1.633 +
1.634 + // emit implementation of reinvokerTarget()
1.635 + mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "reinvokerTarget", "()" + MH_SIG, null, null);
1.636 + mv.visitCode();
1.637 + mv.visitVarInsn(ALOAD, 0);
1.638 + mv.visitFieldInsn(GETFIELD, className, "argL0", JLO_SIG);
1.639 + mv.visitTypeInsn(CHECKCAST, MH);
1.640 + mv.visitInsn(ARETURN);
1.641 + mv.visitMaxs(0, 0);
1.642 + mv.visitEnd();
1.643 +
1.644 + // emit implementation of speciesData()
1.645 + mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "speciesData", MYSPECIES_DATA_SIG, null, null);
1.646 + mv.visitCode();
1.647 + mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
1.648 + mv.visitInsn(ARETURN);
1.649 + mv.visitMaxs(0, 0);
1.650 + mv.visitEnd();
1.651 +
1.652 + // emit clone()
1.653 + mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "clone", makeSignature("", false), null, E_THROWABLE);
1.654 + mv.visitCode();
1.655 + // return speciesData().constructor[0].invokeBasic(mt, lf, argL0, ...)
1.656 + // obtain constructor
1.657 + mv.visitVarInsn(ALOAD, 0);
1.658 + mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
1.659 + mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
1.660 + mv.visitInsn(ICONST_0);
1.661 + mv.visitInsn(AALOAD);
1.662 + // load mt, lf
1.663 + mv.visitVarInsn(ALOAD, 1);
1.664 + mv.visitVarInsn(ALOAD, 2);
1.665 + // put fields on the stack
1.666 + emitPushFields(types, className, mv);
1.667 + // finally, invoke the constructor and return
1.668 + mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types, false));
1.669 + mv.visitInsn(ARETURN);
1.670 + mv.visitMaxs(0, 0);
1.671 + mv.visitEnd();
1.672 +
1.673 + // for each type, emit cloneExtendT()
1.674 + for (Class<?> c : TYPES) {
1.675 + char t = Wrapper.basicTypeChar(c);
1.676 + mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "cloneExtend" + t, makeSignature(String.valueOf(t), false), null, E_THROWABLE);
1.677 + mv.visitCode();
1.678 + // return SPECIES_DATA.extendWithIndex(extensionIndex(t)).constructor[0].invokeBasic(mt, lf, argL0, ..., narg)
1.679 + // obtain constructor
1.680 + mv.visitFieldInsn(GETSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
1.681 + int iconstInsn = ICONST_0 + extensionIndex(t);
1.682 + assert(iconstInsn <= ICONST_5);
1.683 + mv.visitInsn(iconstInsn);
1.684 + mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, "extendWithIndex", BMHSPECIES_DATA_EWI_SIG);
1.685 + mv.visitFieldInsn(GETFIELD, SPECIES_DATA, "constructor", "[" + MH_SIG);
1.686 + mv.visitInsn(ICONST_0);
1.687 + mv.visitInsn(AALOAD);
1.688 + // load mt, lf
1.689 + mv.visitVarInsn(ALOAD, 1);
1.690 + mv.visitVarInsn(ALOAD, 2);
1.691 + // put fields on the stack
1.692 + emitPushFields(types, className, mv);
1.693 + // put narg on stack
1.694 + mv.visitVarInsn(typeLoadOp(t), 3);
1.695 + // finally, invoke the constructor and return
1.696 + mv.visitMethodInsn(INVOKEVIRTUAL, MH, "invokeBasic", makeSignature(types + t, false));
1.697 + mv.visitInsn(ARETURN);
1.698 + mv.visitMaxs(0, 0);
1.699 + mv.visitEnd();
1.700 + }
1.701 +
1.702 + // emit class initializer
1.703 + mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", VOID_SIG, null, null);
1.704 + mv.visitCode();
1.705 + mv.visitLdcInsn(types);
1.706 + mv.visitLdcInsn(Type.getObjectType(className));
1.707 + mv.visitMethodInsn(INVOKESTATIC, SPECIES_DATA, "getForClass", BMHSPECIES_DATA_GFC_SIG);
1.708 + mv.visitFieldInsn(PUTSTATIC, className, "SPECIES_DATA", SPECIES_DATA_SIG);
1.709 + mv.visitInsn(RETURN);
1.710 + mv.visitMaxs(0, 0);
1.711 + mv.visitEnd();
1.712 +
1.713 + cw.visitEnd();
1.714 +
1.715 + // load class
1.716 + final byte[] classFile = cw.toByteArray();
1.717 + InvokerBytecodeGenerator.maybeDump(className, classFile);
1.718 + Class<? extends BoundMethodHandle> bmhClass =
1.719 + //UNSAFE.defineAnonymousClass(BoundMethodHandle.class, classFile, null).asSubclass(BoundMethodHandle.class);
1.720 + UNSAFE.defineClass(className, classFile, 0, classFile.length,
1.721 + BoundMethodHandle.class.getClassLoader(), null)
1.722 + .asSubclass(BoundMethodHandle.class);
1.723 + UNSAFE.ensureClassInitialized(bmhClass);
1.724 +
1.725 + return bmhClass;
1.726 + }
1.727 +
1.728 + private static int typeLoadOp(char t) {
1.729 + switch (t) {
1.730 + case 'L': return ALOAD;
1.731 + case 'I': return ILOAD;
1.732 + case 'J': return LLOAD;
1.733 + case 'F': return FLOAD;
1.734 + case 'D': return DLOAD;
1.735 + default : throw new InternalError("unrecognized type " + t);
1.736 + }
1.737 + }
1.738 +
1.739 + private static void emitPushFields(String types, String className, MethodVisitor mv) {
1.740 + for (int i = 0; i < types.length(); ++i) {
1.741 + char tc = types.charAt(i);
1.742 + mv.visitVarInsn(ALOAD, 0);
1.743 + mv.visitFieldInsn(GETFIELD, className, makeFieldName(types, i), typeSig(tc));
1.744 + }
1.745 + }
1.746 +
1.747 + static String typeSig(char t) {
1.748 + return t == 'L' ? JLO_SIG : String.valueOf(t);
1.749 + }
1.750 +
1.751 + //
1.752 + // Getter MH generation.
1.753 + //
1.754 +
1.755 + private static MethodHandle makeGetter(Class<?> cbmhClass, String types, int index) {
1.756 + String fieldName = makeFieldName(types, index);
1.757 + Class<?> fieldType = Wrapper.forBasicType(types.charAt(index)).primitiveType();
1.758 + try {
1.759 + return LOOKUP.findGetter(cbmhClass, fieldName, fieldType);
1.760 + } catch (NoSuchFieldException | IllegalAccessException e) {
1.761 + throw newInternalError(e);
1.762 + }
1.763 + }
1.764 +
1.765 + static MethodHandle[] makeGetters(Class<?> cbmhClass, String types, MethodHandle[] mhs) {
1.766 + if (mhs == null) mhs = new MethodHandle[types.length()];
1.767 + for (int i = 0; i < mhs.length; ++i) {
1.768 + mhs[i] = makeGetter(cbmhClass, types, i);
1.769 + assert(mhs[i].internalMemberName().getDeclaringClass() == cbmhClass);
1.770 + }
1.771 + return mhs;
1.772 + }
1.773 +
1.774 + static MethodHandle[] makeCtors(Class<? extends BoundMethodHandle> cbmh, String types, MethodHandle mhs[]) {
1.775 + if (mhs == null) mhs = new MethodHandle[1];
1.776 + mhs[0] = makeCbmhCtor(cbmh, types);
1.777 + return mhs;
1.778 + }
1.779 +
1.780 + //
1.781 + // Auxiliary methods.
1.782 + //
1.783 +
1.784 + static SpeciesData speciesDataFromConcreteBMHClass(Class<? extends BoundMethodHandle> cbmh) {
1.785 + try {
1.786 + Field F_SPECIES_DATA = cbmh.getDeclaredField("SPECIES_DATA");
1.787 + return (SpeciesData) F_SPECIES_DATA.get(null);
1.788 + } catch (ReflectiveOperationException ex) {
1.789 + throw newInternalError(ex);
1.790 + }
1.791 + }
1.792 +
1.793 + /**
1.794 + * Field names in concrete BMHs adhere to this pattern:
1.795 + * arg + type + index
1.796 + * where type is a single character (L, I, J, F, D).
1.797 + */
1.798 + private static String makeFieldName(String types, int index) {
1.799 + assert index >= 0 && index < types.length();
1.800 + return "arg" + types.charAt(index) + index;
1.801 + }
1.802 +
1.803 + private static String makeSignature(String types, boolean ctor) {
1.804 + StringBuilder buf = new StringBuilder(SIG_INCIPIT);
1.805 + for (char c : types.toCharArray()) {
1.806 + buf.append(typeSig(c));
1.807 + }
1.808 + return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
1.809 + }
1.810 +
1.811 + static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
1.812 + try {
1.813 + return linkConstructor(LOOKUP.findConstructor(cbmh, MethodType.fromMethodDescriptorString(makeSignature(types, true), null)));
1.814 + } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
1.815 + throw newInternalError(e);
1.816 + }
1.817 + }
1.818 +
1.819 + /**
1.820 + * Wrap a constructor call in a {@link LambdaForm}.
1.821 + *
1.822 + * If constructors ({@code <init>} methods) are called in LFs, problems might arise if the LFs
1.823 + * are turned into bytecode, because the call to the allocator is routed through an MH, and the
1.824 + * verifier cannot find a {@code NEW} instruction preceding the {@code INVOKESPECIAL} to
1.825 + * {@code <init>}. To avoid this, we add an indirection by invoking {@code <init>} through
1.826 + * {@link MethodHandle#linkToSpecial}.
1.827 + *
1.828 + * The last {@link LambdaForm.Name Name} in the argument's form is expected to be the {@code void}
1.829 + * result of the {@code <init>} invocation. This entry is replaced.
1.830 + */
1.831 + private static MethodHandle linkConstructor(MethodHandle cmh) {
1.832 + final LambdaForm lf = cmh.form;
1.833 + final int initNameIndex = lf.names.length - 1;
1.834 + final Name initName = lf.names[initNameIndex];
1.835 + final MemberName ctorMN = initName.function.member;
1.836 + final MethodType ctorMT = ctorMN.getInvocationType();
1.837 +
1.838 + // obtain function member (call target)
1.839 + // linker method type replaces initial parameter (BMH species) with BMH to avoid naming a species (anonymous class!)
1.840 + final MethodType linkerMT = ctorMT.changeParameterType(0, BoundMethodHandle.class).appendParameterTypes(MemberName.class);
1.841 + MemberName linkerMN = new MemberName(MethodHandle.class, "linkToSpecial", linkerMT, REF_invokeStatic);
1.842 + try {
1.843 + linkerMN = MemberName.getFactory().resolveOrFail(REF_invokeStatic, linkerMN, null, NoSuchMethodException.class);
1.844 + assert(linkerMN.isStatic());
1.845 + } catch (ReflectiveOperationException ex) {
1.846 + throw newInternalError(ex);
1.847 + }
1.848 + // extend arguments array
1.849 + Object[] newArgs = Arrays.copyOf(initName.arguments, initName.arguments.length + 1);
1.850 + newArgs[newArgs.length - 1] = ctorMN;
1.851 + // replace function
1.852 + final NamedFunction nf = new NamedFunction(linkerMN);
1.853 + final Name linkedCtor = new Name(nf, newArgs);
1.854 + linkedCtor.initIndex(initNameIndex);
1.855 + lf.names[initNameIndex] = linkedCtor;
1.856 + return cmh;
1.857 + }
1.858 +
1.859 + }
1.860 +
1.861 + private static final Lookup LOOKUP = Lookup.IMPL_LOOKUP;
1.862 +
1.863 + /**
1.864 + * All subclasses must provide such a value describing their type signature.
1.865 + */
1.866 + static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
1.867 +}