|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.lang.invoke; |
|
|
|
import jdk.internal.access.SharedSecrets; |
|
import jdk.internal.loader.BootLoader; |
|
import jdk.internal.org.objectweb.asm.ClassWriter; |
|
import jdk.internal.org.objectweb.asm.FieldVisitor; |
|
import jdk.internal.org.objectweb.asm.MethodVisitor; |
|
import jdk.internal.vm.annotation.Stable; |
|
import sun.invoke.util.BytecodeName; |
|
|
|
import java.lang.reflect.Constructor; |
|
import java.lang.reflect.Field; |
|
import java.lang.reflect.Modifier; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.security.ProtectionDomain; |
|
import java.util.ArrayList; |
|
import java.util.Collections; |
|
import java.util.List; |
|
import java.util.Objects; |
|
import java.util.concurrent.ConcurrentHashMap; |
|
import java.util.function.Function; |
|
|
|
import static java.lang.invoke.LambdaForm.*; |
|
import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic; |
|
import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic; |
|
import static java.lang.invoke.MethodHandleStatics.*; |
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; |
|
import static jdk.internal.org.objectweb.asm.Opcodes.*; |
|
|
|
/** |
|
* Class specialization code. |
|
* @param <T> top class under which species classes are created. |
|
* @param <K> key which identifies individual specializations. |
|
* @param <S> species data type. |
|
*/ |
|
|
|
abstract class ClassSpecializer<T,K,S extends ClassSpecializer<T,K,S>.SpeciesData> { |
|
private final Class<T> topClass; |
|
private final Class<K> keyType; |
|
private final Class<S> metaType; |
|
private final MemberName sdAccessor; |
|
private final String sdFieldName; |
|
private final List<MemberName> transformMethods; |
|
private final MethodType baseConstructorType; |
|
private final S topSpecies; |
|
private final ConcurrentHashMap<K, Object> cache = new ConcurrentHashMap<>(); |
|
private final Factory factory; |
|
private @Stable boolean topClassIsSuper; |
|
|
|
|
|
public final Class<T> topClass() { return topClass; } |
|
|
|
|
|
public final Class<K> keyType() { return keyType; } |
|
|
|
|
|
public final Class<S> metaType() { return metaType; } |
|
|
|
|
|
|
|
|
|
*/ |
|
protected MethodType baseConstructorType() { return baseConstructorType; } |
|
|
|
|
|
protected final S topSpecies() { return topSpecies; } |
|
|
|
|
|
protected final List<MemberName> transformMethods() { return transformMethods; } |
|
|
|
|
|
protected final Factory factory() { return factory; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected ClassSpecializer(Class<T> topClass, |
|
Class<K> keyType, |
|
Class<S> metaType, |
|
MethodType baseConstructorType, |
|
MemberName sdAccessor, |
|
String sdFieldName, |
|
List<MemberName> transformMethods) { |
|
this.topClass = topClass; |
|
this.keyType = keyType; |
|
this.metaType = metaType; |
|
this.sdAccessor = sdAccessor; |
|
this.transformMethods = List.copyOf(transformMethods); |
|
this.sdFieldName = sdFieldName; |
|
this.baseConstructorType = baseConstructorType.changeReturnType(void.class); |
|
this.factory = makeFactory(); |
|
K tsk = topSpeciesKey(); |
|
S topSpecies = null; |
|
if (tsk != null && topSpecies == null) { |
|
|
|
topSpecies = findSpecies(tsk); |
|
} |
|
this.topSpecies = topSpecies; |
|
} |
|
|
|
|
|
protected static <T> Constructor<T> reflectConstructor(Class<T> defc, Class<?>... ptypes) { |
|
try { |
|
return defc.getDeclaredConstructor(ptypes); |
|
} catch (NoSuchMethodException ex) { |
|
throw newIAE(defc.getName()+"("+MethodType.methodType(void.class, ptypes)+")", ex); |
|
} |
|
} |
|
|
|
protected static Field reflectField(Class<?> defc, String name) { |
|
try { |
|
return defc.getDeclaredField(name); |
|
} catch (NoSuchFieldException ex) { |
|
throw newIAE(defc.getName()+"."+name, ex); |
|
} |
|
} |
|
|
|
private static RuntimeException newIAE(String message, Throwable cause) { |
|
return new IllegalArgumentException(message, cause); |
|
} |
|
|
|
private static final Function<Object, Object> CREATE_RESERVATION = new Function<>() { |
|
@Override |
|
public Object apply(Object key) { |
|
return new Object(); |
|
} |
|
}; |
|
|
|
public final S findSpecies(K key) { |
|
// Note: Species instantiation may throw VirtualMachineError because of |
|
// code cache overflow. If this happens the species bytecode may be |
|
// loaded but not linked to its species metadata (with MH's etc). |
|
// That will cause a throw out of Factory.loadSpecies. |
|
// |
|
// In a later attempt to get the same species, the already-loaded |
|
// class will be present in the system dictionary, causing an |
|
// error when the species generator tries to reload it. |
|
// We try to detect this case and link the pre-existing code. |
|
// |
|
// Although it would be better to start fresh by loading a new |
|
// copy, we have to salvage the previously loaded but broken code. |
|
// (As an alternative, we might spin a new class with a new name, |
|
// or use the anonymous class mechanism.) |
|
// |
|
// In the end, as long as everybody goes through this findSpecies method, |
|
// it will ensure only one SpeciesData will be set successfully on a |
|
// concrete class if ever. |
|
// The concrete class is published via SpeciesData instance |
|
|
|
Object speciesDataOrReservation = cache.computeIfAbsent(key, CREATE_RESERVATION); |
|
// Separating the creation of a placeholder SpeciesData instance above |
|
// from the loading and linking a real one below ensures we can never |
|
|
|
S speciesData; |
|
if (speciesDataOrReservation.getClass() == Object.class) { |
|
synchronized (speciesDataOrReservation) { |
|
Object existingSpeciesData = cache.get(key); |
|
if (existingSpeciesData == speciesDataOrReservation) { // won the race |
|
|
|
speciesData = newSpeciesData(key); |
|
|
|
speciesData = factory.loadSpecies(speciesData); |
|
if (!cache.replace(key, existingSpeciesData, speciesData)) { |
|
throw newInternalError("Concurrent loadSpecies"); |
|
} |
|
} else { |
|
speciesData = metaType.cast(existingSpeciesData); |
|
} |
|
} |
|
} else { |
|
speciesData = metaType.cast(speciesDataOrReservation); |
|
} |
|
assert(speciesData != null && speciesData.isResolved()); |
|
return speciesData; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public abstract class SpeciesData { |
|
// Bootstrapping requires circular relations Class -> SpeciesData -> Class |
|
|
|
private final K key; |
|
private final List<Class<?>> fieldTypes; |
|
@Stable private Class<? extends T> speciesCode; |
|
@Stable private List<MethodHandle> factories; |
|
@Stable private List<MethodHandle> getters; |
|
@Stable private List<LambdaForm.NamedFunction> nominalGetters; |
|
@Stable private final MethodHandle[] transformHelpers = new MethodHandle[transformMethods.size()]; |
|
|
|
protected SpeciesData(K key) { |
|
this.key = keyType.cast(Objects.requireNonNull(key)); |
|
List<Class<?>> types = deriveFieldTypes(key); |
|
this.fieldTypes = List.copyOf(types); |
|
} |
|
|
|
public final K key() { |
|
return key; |
|
} |
|
|
|
protected final List<Class<?>> fieldTypes() { |
|
return fieldTypes; |
|
} |
|
|
|
protected final int fieldCount() { |
|
return fieldTypes.size(); |
|
} |
|
|
|
protected ClassSpecializer<T,K,S> outer() { |
|
return ClassSpecializer.this; |
|
} |
|
|
|
protected final boolean isResolved() { |
|
return speciesCode != null && factories != null && !factories.isEmpty(); |
|
} |
|
|
|
@Override public String toString() { |
|
return metaType.getSimpleName() + "[" + key.toString() + " => " + (isResolved() ? speciesCode.getSimpleName() : "UNRESOLVED") + "]"; |
|
} |
|
|
|
@Override |
|
public int hashCode() { |
|
return key.hashCode(); |
|
} |
|
|
|
@Override |
|
public boolean equals(Object obj) { |
|
if (!(obj instanceof ClassSpecializer.SpeciesData)) { |
|
return false; |
|
} |
|
@SuppressWarnings("rawtypes") |
|
ClassSpecializer.SpeciesData that = (ClassSpecializer.SpeciesData) obj; |
|
return this.outer() == that.outer() && this.key.equals(that.key); |
|
} |
|
|
|
|
|
protected final Class<? extends T> speciesCode() { |
|
return Objects.requireNonNull(speciesCode); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected MethodHandle getter(int i) { |
|
return getters.get(i); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected LambdaForm.NamedFunction getterFunction(int i) { |
|
LambdaForm.NamedFunction nf = nominalGetters.get(i); |
|
assert(nf.memberDeclaringClassOrNull() == speciesCode()); |
|
assert(nf.returnType() == BasicType.basicType(fieldTypes.get(i))); |
|
return nf; |
|
} |
|
|
|
protected List<LambdaForm.NamedFunction> getterFunctions() { |
|
return nominalGetters; |
|
} |
|
|
|
protected List<MethodHandle> getters() { |
|
return getters; |
|
} |
|
|
|
protected MethodHandle factory() { |
|
return factories.get(0); |
|
} |
|
|
|
protected MethodHandle transformHelper(int whichtm) { |
|
MethodHandle mh = transformHelpers[whichtm]; |
|
if (mh != null) return mh; |
|
mh = deriveTransformHelper(transformMethods().get(whichtm), whichtm); |
|
// Do a little type checking before we start using the MH. |
|
|
|
final MethodType mt = transformHelperType(whichtm); |
|
mh = mh.asType(mt); |
|
return transformHelpers[whichtm] = mh; |
|
} |
|
|
|
private final MethodType transformHelperType(int whichtm) { |
|
MemberName tm = transformMethods().get(whichtm); |
|
ArrayList<Class<?>> args = new ArrayList<>(); |
|
ArrayList<Class<?>> fields = new ArrayList<>(); |
|
Collections.addAll(args, tm.getParameterTypes()); |
|
fields.addAll(fieldTypes()); |
|
List<Class<?>> helperArgs = deriveTransformHelperArguments(tm, whichtm, args, fields); |
|
return MethodType.methodType(tm.getReturnType(), helperArgs); |
|
} |
|
|
|
// Hooks for subclasses: |
|
|
|
|
|
|
|
|
|
*/ |
|
protected abstract List<Class<?>> deriveFieldTypes(K key); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected abstract MethodHandle deriveTransformHelper(MemberName transform, int whichtm); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected abstract <X> List<X> deriveTransformHelperArguments(MemberName transform, int whichtm, |
|
List<X> args, List<X> fields); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected String deriveClassName() { |
|
return outer().topClass().getName() + "$Species_" + deriveTypeString(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected String deriveTypeString() { |
|
List<Class<?>> types = fieldTypes(); |
|
StringBuilder buf = new StringBuilder(); |
|
StringBuilder end = new StringBuilder(); |
|
for (Class<?> type : types) { |
|
BasicType basicType = BasicType.basicType(type); |
|
if (basicType.basicTypeClass() == type) { |
|
buf.append(basicType.basicTypeChar()); |
|
} else { |
|
buf.append('V'); |
|
end.append(classSig(type)); |
|
} |
|
} |
|
String typeString; |
|
if (end.length() > 0) { |
|
typeString = BytecodeName.toBytecodeName(buf.append("_").append(end).toString()); |
|
} else { |
|
typeString = buf.toString(); |
|
} |
|
return LambdaForm.shortenSignature(typeString); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected Class<? extends T> deriveSuperClass() { |
|
final Class<T> topc = topClass(); |
|
if (!topClassIsSuper) { |
|
try { |
|
final Constructor<T> con = reflectConstructor(topc, baseConstructorType().parameterArray()); |
|
if (!topc.isInterface() && !Modifier.isPrivate(con.getModifiers())) { |
|
topClassIsSuper = true; |
|
} |
|
} catch (Exception|InternalError ex) { |
|
// fall through... |
|
} |
|
if (!topClassIsSuper) { |
|
throw newInternalError("must override if the top class cannot serve as a super class"); |
|
} |
|
} |
|
return topc; |
|
} |
|
} |
|
|
|
protected abstract S newSpeciesData(K key); |
|
|
|
protected K topSpeciesKey() { |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public class Factory { |
|
|
|
|
|
*/ |
|
Factory() {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
S loadSpecies(S speciesData) { |
|
String className = speciesData.deriveClassName(); |
|
assert(className.indexOf('/') < 0) : className; |
|
Class<?> salvage = null; |
|
try { |
|
salvage = BootLoader.loadClassOrNull(className); |
|
} catch (Error ex) { |
|
// ignore |
|
} finally { |
|
traceSpeciesType(className, salvage); |
|
} |
|
final Class<? extends T> speciesCode; |
|
if (salvage != null) { |
|
speciesCode = salvage.asSubclass(topClass()); |
|
linkSpeciesDataToCode(speciesData, speciesCode); |
|
linkCodeToSpeciesData(speciesCode, speciesData, true); |
|
} else { |
|
|
|
try { |
|
speciesCode = generateConcreteSpeciesCode(className, speciesData); |
|
|
|
linkSpeciesDataToCode(speciesData, speciesCode); |
|
|
|
linkCodeToSpeciesData(speciesCode, speciesData, false); |
|
} catch (Error ex) { |
|
// We can get here if there is a race condition loading a class. |
|
|
|
throw ex; |
|
} |
|
} |
|
|
|
if (!speciesData.isResolved()) { |
|
throw newInternalError("bad species class linkage for " + className + ": " + speciesData); |
|
} |
|
assert(speciesData == loadSpeciesDataFromCode(speciesCode)); |
|
return speciesData; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
Class<? extends T> generateConcreteSpeciesCode(String className, ClassSpecializer<T,K,S>.SpeciesData speciesData) { |
|
byte[] classFile = generateConcreteSpeciesCodeFile(className, speciesData); |
|
|
|
|
|
InvokerBytecodeGenerator.maybeDump(classBCName(className), classFile); |
|
ClassLoader cl = topClass.getClassLoader(); |
|
ProtectionDomain pd = null; |
|
if (cl != null) { |
|
pd = AccessController.doPrivileged( |
|
new PrivilegedAction<>() { |
|
@Override |
|
public ProtectionDomain run() { |
|
return topClass().getProtectionDomain(); |
|
} |
|
}); |
|
} |
|
Class<?> speciesCode = SharedSecrets.getJavaLangAccess() |
|
.defineClass(cl, className, classFile, pd, "_ClassSpecializer_generateConcreteSpeciesCode"); |
|
return speciesCode.asSubclass(topClass()); |
|
} |
|
|
|
|
|
private final String SPECIES_DATA = classBCName(metaType); |
|
private final String SPECIES_DATA_SIG = classSig(SPECIES_DATA); |
|
private final String SPECIES_DATA_NAME = sdAccessor.getName(); |
|
private final int SPECIES_DATA_MODS = sdAccessor.getModifiers(); |
|
private final List<String> TRANSFORM_NAMES; |
|
private final List<MethodType> TRANSFORM_TYPES; |
|
private final List<Integer> TRANSFORM_MODS; |
|
{ |
|
|
|
List<String> tns = new ArrayList<>(); |
|
List<MethodType> tts = new ArrayList<>(); |
|
List<Integer> tms = new ArrayList<>(); |
|
for (int i = 0; i < transformMethods.size(); i++) { |
|
MemberName tm = transformMethods.get(i); |
|
tns.add(tm.getName()); |
|
final MethodType tt = tm.getMethodType(); |
|
tts.add(tt); |
|
tms.add(tm.getModifiers()); |
|
} |
|
TRANSFORM_NAMES = List.of(tns.toArray(new String[0])); |
|
TRANSFORM_TYPES = List.of(tts.toArray(new MethodType[0])); |
|
TRANSFORM_MODS = List.of(tms.toArray(new Integer[0])); |
|
} |
|
private static final int ACC_PPP = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED; |
|
|
|
|
|
byte[] generateConcreteSpeciesCodeFile(String className0, ClassSpecializer<T,K,S>.SpeciesData speciesData) { |
|
final String className = classBCName(className0); |
|
final String superClassName = classBCName(speciesData.deriveSuperClass()); |
|
|
|
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); |
|
final int NOT_ACC_PUBLIC = 0; |
|
cw.visit(V1_6, NOT_ACC_PUBLIC + ACC_FINAL + ACC_SUPER, className, null, superClassName, null); |
|
|
|
final String sourceFile = className.substring(className.lastIndexOf('.')+1); |
|
cw.visitSource(sourceFile, null); |
|
|
|
|
|
FieldVisitor fw = cw.visitField(NOT_ACC_PUBLIC + ACC_STATIC, sdFieldName, SPECIES_DATA_SIG, null, null); |
|
fw.visitAnnotation(STABLE_SIG, true); |
|
fw.visitEnd(); |
|
|
|
|
|
class Var { |
|
final int index; |
|
final String name; |
|
final Class<?> type; |
|
final String desc; |
|
final BasicType basicType; |
|
final int slotIndex; |
|
Var(int index, int slotIndex) { |
|
this.index = index; |
|
this.slotIndex = slotIndex; |
|
name = null; type = null; desc = null; |
|
basicType = BasicType.V_TYPE; |
|
} |
|
Var(String name, Class<?> type, Var prev) { |
|
int slotIndex = prev.nextSlotIndex(); |
|
int index = prev.nextIndex(); |
|
if (name == null) name = "x"; |
|
if (name.endsWith("#")) |
|
name = name.substring(0, name.length()-1) + index; |
|
assert(!type.equals(void.class)); |
|
String desc = classSig(type); |
|
BasicType basicType = BasicType.basicType(type); |
|
this.index = index; |
|
this.name = name; |
|
this.type = type; |
|
this.desc = desc; |
|
this.basicType = basicType; |
|
this.slotIndex = slotIndex; |
|
} |
|
Var lastOf(List<Var> vars) { |
|
int n = vars.size(); |
|
return (n == 0 ? this : vars.get(n-1)); |
|
} |
|
<X> List<Var> fromTypes(List<X> types) { |
|
Var prev = this; |
|
ArrayList<Var> result = new ArrayList<>(types.size()); |
|
int i = 0; |
|
for (X x : types) { |
|
String vn = name; |
|
Class<?> vt; |
|
if (x instanceof Class) { |
|
vt = (Class<?>) x; |
|
|
|
assert((vn = vn + "_" + (i++)) != null); |
|
} else { |
|
@SuppressWarnings("unchecked") |
|
Var v = (Var) x; |
|
vn = v.name; |
|
vt = v.type; |
|
} |
|
prev = new Var(vn, vt, prev); |
|
result.add(prev); |
|
} |
|
return result; |
|
} |
|
|
|
int slotSize() { return basicType.basicTypeSlots(); } |
|
int nextIndex() { return index + (slotSize() == 0 ? 0 : 1); } |
|
int nextSlotIndex() { return slotIndex >= 0 ? slotIndex + slotSize() : slotIndex; } |
|
boolean isInHeap() { return slotIndex < 0; } |
|
void emitVarInstruction(int asmop, MethodVisitor mv) { |
|
if (asmop == ALOAD) |
|
asmop = typeLoadOp(basicType.basicTypeChar()); |
|
else |
|
throw new AssertionError("bad op="+asmop+" for desc="+desc); |
|
mv.visitVarInsn(asmop, slotIndex); |
|
} |
|
public void emitFieldInsn(int asmop, MethodVisitor mv) { |
|
mv.visitFieldInsn(asmop, className, name, desc); |
|
} |
|
} |
|
|
|
final Var NO_THIS = new Var(0, 0), |
|
AFTER_THIS = new Var(0, 1), |
|
IN_HEAP = new Var(0, -1); |
|
|
|
|
|
final List<Class<?>> fieldTypes = speciesData.fieldTypes(); |
|
final List<Var> fields = new ArrayList<>(fieldTypes.size()); |
|
{ |
|
Var nextF = IN_HEAP; |
|
for (Class<?> ft : fieldTypes) { |
|
String fn = chooseFieldName(ft, nextF.nextIndex()); |
|
nextF = new Var(fn, ft, nextF); |
|
fields.add(nextF); |
|
} |
|
} |
|
|
|
|
|
for (Var field : fields) { |
|
cw.visitField(ACC_FINAL, field.name, field.desc, null, null).visitEnd(); |
|
} |
|
|
|
MethodVisitor mv; |
|
|
|
|
|
mv = cw.visitMethod((SPECIES_DATA_MODS & ACC_PPP) + ACC_FINAL, |
|
SPECIES_DATA_NAME, "()" + SPECIES_DATA_SIG, null, null); |
|
mv.visitCode(); |
|
mv.visitFieldInsn(GETSTATIC, className, sdFieldName, SPECIES_DATA_SIG); |
|
mv.visitInsn(ARETURN); |
|
mv.visitMaxs(0, 0); |
|
mv.visitEnd(); |
|
|
|
|
|
MethodType superCtorType = ClassSpecializer.this.baseConstructorType(); |
|
MethodType thisCtorType = superCtorType.appendParameterTypes(fieldTypes); |
|
|
|
|
|
{ |
|
mv = cw.visitMethod(ACC_PRIVATE, |
|
"<init>", methodSig(thisCtorType), null, null); |
|
mv.visitCode(); |
|
mv.visitVarInsn(ALOAD, 0); |
|
|
|
final List<Var> ctorArgs = AFTER_THIS.fromTypes(superCtorType.parameterList()); |
|
for (Var ca : ctorArgs) { |
|
ca.emitVarInstruction(ALOAD, mv); |
|
} |
|
|
|
|
|
mv.visitMethodInsn(INVOKESPECIAL, superClassName, |
|
"<init>", methodSig(superCtorType), false); |
|
|
|
|
|
Var lastFV = AFTER_THIS.lastOf(ctorArgs); |
|
for (Var f : fields) { |
|
// this.argL1 = argL1 |
|
mv.visitVarInsn(ALOAD, 0); |
|
lastFV = new Var(f.name, f.type, lastFV); |
|
lastFV.emitVarInstruction(ALOAD, mv); |
|
f.emitFieldInsn(PUTFIELD, mv); |
|
} |
|
|
|
mv.visitInsn(RETURN); |
|
mv.visitMaxs(0, 0); |
|
mv.visitEnd(); |
|
} |
|
|
|
|
|
{ |
|
MethodType ftryType = thisCtorType.changeReturnType(topClass()); |
|
mv = cw.visitMethod(NOT_ACC_PUBLIC + ACC_STATIC, |
|
"make", methodSig(ftryType), null, null); |
|
mv.visitCode(); |
|
|
|
mv.visitTypeInsn(NEW, className); |
|
mv.visitInsn(DUP); |
|
|
|
for (Var v : NO_THIS.fromTypes(ftryType.parameterList())) { |
|
v.emitVarInstruction(ALOAD, mv); |
|
} |
|
|
|
|
|
mv.visitMethodInsn(INVOKESPECIAL, className, |
|
"<init>", methodSig(thisCtorType), false); |
|
mv.visitInsn(ARETURN); |
|
mv.visitMaxs(0, 0); |
|
mv.visitEnd(); |
|
} |
|
|
|
// For each transform, emit the customized override of the transform method. |
|
// This method mixes together some incoming arguments (from the transform's |
|
// static type signature) with the field types themselves, and passes |
|
// the resulting mish-mosh of values to a method handle produced by |
|
// the species itself. (Typically this method handle is the factory |
|
|
|
for (int whichtm = 0; whichtm < TRANSFORM_NAMES.size(); whichtm++) { |
|
final String TNAME = TRANSFORM_NAMES.get(whichtm); |
|
final MethodType TTYPE = TRANSFORM_TYPES.get(whichtm); |
|
final int TMODS = TRANSFORM_MODS.get(whichtm); |
|
mv = cw.visitMethod((TMODS & ACC_PPP) | ACC_FINAL, |
|
TNAME, TTYPE.toMethodDescriptorString(), null, E_THROWABLE); |
|
mv.visitCode(); |
|
// return a call to the corresponding "transform helper", something like this: |
|
|
|
mv.visitFieldInsn(GETSTATIC, className, |
|
sdFieldName, SPECIES_DATA_SIG); |
|
emitIntConstant(whichtm, mv); |
|
mv.visitMethodInsn(INVOKEVIRTUAL, SPECIES_DATA, |
|
"transformHelper", "(I)" + MH_SIG, false); |
|
|
|
List<Var> targs = AFTER_THIS.fromTypes(TTYPE.parameterList()); |
|
List<Var> tfields = new ArrayList<>(fields); |
|
|
|
List<Var> helperArgs = speciesData.deriveTransformHelperArguments(transformMethods.get(whichtm), whichtm, targs, tfields); |
|
List<Class<?>> helperTypes = new ArrayList<>(helperArgs.size()); |
|
for (Var ha : helperArgs) { |
|
helperTypes.add(ha.basicType.basicTypeClass()); |
|
if (ha.isInHeap()) { |
|
assert(tfields.contains(ha)); |
|
mv.visitVarInsn(ALOAD, 0); |
|
ha.emitFieldInsn(GETFIELD, mv); |
|
} else { |
|
assert(targs.contains(ha)); |
|
ha.emitVarInstruction(ALOAD, mv); |
|
} |
|
} |
|
|
|
|
|
final Class<?> rtype = TTYPE.returnType(); |
|
final BasicType rbt = BasicType.basicType(rtype); |
|
MethodType invokeBasicType = MethodType.methodType(rbt.basicTypeClass(), helperTypes); |
|
mv.visitMethodInsn(INVOKEVIRTUAL, MH, |
|
"invokeBasic", methodSig(invokeBasicType), false); |
|
if (rbt == BasicType.L_TYPE) { |
|
mv.visitTypeInsn(CHECKCAST, classBCName(rtype)); |
|
mv.visitInsn(ARETURN); |
|
} else { |
|
throw newInternalError("NYI: transform of type "+rtype); |
|
} |
|
mv.visitMaxs(0, 0); |
|
mv.visitEnd(); |
|
} |
|
|
|
cw.visitEnd(); |
|
|
|
return cw.toByteArray(); |
|
} |
|
|
|
private int typeLoadOp(char t) { |
|
return switch (t) { |
|
case 'L' -> ALOAD; |
|
case 'I' -> ILOAD; |
|
case 'J' -> LLOAD; |
|
case 'F' -> FLOAD; |
|
case 'D' -> DLOAD; |
|
default -> throw newInternalError("unrecognized type " + t); |
|
}; |
|
} |
|
|
|
private void emitIntConstant(int con, MethodVisitor mv) { |
|
if (ICONST_M1 - ICONST_0 <= con && con <= ICONST_5 - ICONST_0) |
|
mv.visitInsn(ICONST_0 + con); |
|
else if (con == (byte) con) |
|
mv.visitIntInsn(BIPUSH, con); |
|
else if (con == (short) con) |
|
mv.visitIntInsn(SIPUSH, con); |
|
else { |
|
mv.visitLdcInsn(con); |
|
} |
|
|
|
} |
|
|
|
// |
|
// Getter MH generation. |
|
// |
|
|
|
private MethodHandle findGetter(Class<?> speciesCode, List<Class<?>> types, int index) { |
|
Class<?> fieldType = types.get(index); |
|
String fieldName = chooseFieldName(fieldType, index); |
|
try { |
|
return IMPL_LOOKUP.findGetter(speciesCode, fieldName, fieldType); |
|
} catch (NoSuchFieldException | IllegalAccessException e) { |
|
throw newInternalError(e); |
|
} |
|
} |
|
|
|
private List<MethodHandle> findGetters(Class<?> speciesCode, List<Class<?>> types) { |
|
MethodHandle[] mhs = new MethodHandle[types.size()]; |
|
for (int i = 0; i < mhs.length; ++i) { |
|
mhs[i] = findGetter(speciesCode, types, i); |
|
assert(mhs[i].internalMemberName().getDeclaringClass() == speciesCode); |
|
} |
|
return List.of(mhs); |
|
} |
|
|
|
private List<MethodHandle> findFactories(Class<? extends T> speciesCode, List<Class<?>> types) { |
|
MethodHandle[] mhs = new MethodHandle[1]; |
|
mhs[0] = findFactory(speciesCode, types); |
|
return List.of(mhs); |
|
} |
|
|
|
List<LambdaForm.NamedFunction> makeNominalGetters(List<Class<?>> types, List<MethodHandle> getters) { |
|
LambdaForm.NamedFunction[] nfs = new LambdaForm.NamedFunction[types.size()]; |
|
for (int i = 0; i < nfs.length; ++i) { |
|
nfs[i] = new LambdaForm.NamedFunction(getters.get(i)); |
|
} |
|
return List.of(nfs); |
|
} |
|
|
|
// |
|
// Auxiliary methods. |
|
// |
|
|
|
protected void linkSpeciesDataToCode(ClassSpecializer<T,K,S>.SpeciesData speciesData, Class<? extends T> speciesCode) { |
|
speciesData.speciesCode = speciesCode.asSubclass(topClass); |
|
final List<Class<?>> types = speciesData.fieldTypes; |
|
speciesData.factories = this.findFactories(speciesCode, types); |
|
speciesData.getters = this.findGetters(speciesCode, types); |
|
speciesData.nominalGetters = this.makeNominalGetters(types, speciesData.getters); |
|
} |
|
|
|
private Field reflectSDField(Class<? extends T> speciesCode) { |
|
final Field field = reflectField(speciesCode, sdFieldName); |
|
assert(field.getType() == metaType); |
|
assert(Modifier.isStatic(field.getModifiers())); |
|
return field; |
|
} |
|
|
|
private S readSpeciesDataFromCode(Class<? extends T> speciesCode) { |
|
try { |
|
MemberName sdField = IMPL_LOOKUP.resolveOrFail(REF_getStatic, speciesCode, sdFieldName, metaType); |
|
Object base = MethodHandleNatives.staticFieldBase(sdField); |
|
long offset = MethodHandleNatives.staticFieldOffset(sdField); |
|
UNSAFE.loadFence(); |
|
return metaType.cast(UNSAFE.getReference(base, offset)); |
|
} catch (Error err) { |
|
throw err; |
|
} catch (Exception ex) { |
|
throw newInternalError("Failed to load speciesData from speciesCode: " + speciesCode.getName(), ex); |
|
} catch (Throwable t) { |
|
throw uncaughtException(t); |
|
} |
|
} |
|
|
|
protected S loadSpeciesDataFromCode(Class<? extends T> speciesCode) { |
|
if (speciesCode == topClass()) { |
|
return topSpecies; |
|
} |
|
S result = readSpeciesDataFromCode(speciesCode); |
|
if (result.outer() != ClassSpecializer.this) { |
|
throw newInternalError("wrong class"); |
|
} |
|
return result; |
|
} |
|
|
|
protected void linkCodeToSpeciesData(Class<? extends T> speciesCode, ClassSpecializer<T,K,S>.SpeciesData speciesData, boolean salvage) { |
|
try { |
|
assert(readSpeciesDataFromCode(speciesCode) == null || |
|
(salvage && readSpeciesDataFromCode(speciesCode).equals(speciesData))); |
|
|
|
MemberName sdField = IMPL_LOOKUP.resolveOrFail(REF_putStatic, speciesCode, sdFieldName, metaType); |
|
Object base = MethodHandleNatives.staticFieldBase(sdField); |
|
long offset = MethodHandleNatives.staticFieldOffset(sdField); |
|
UNSAFE.storeFence(); |
|
UNSAFE.putReference(base, offset, speciesData); |
|
UNSAFE.storeFence(); |
|
} catch (Error err) { |
|
throw err; |
|
} catch (Exception ex) { |
|
throw newInternalError("Failed to link speciesData to speciesCode: " + speciesCode.getName(), ex); |
|
} catch (Throwable t) { |
|
throw uncaughtException(t); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected String chooseFieldName(Class<?> type, int index) { |
|
BasicType bt = BasicType.basicType(type); |
|
return "" + bt.basicTypeChar() + index; |
|
} |
|
|
|
MethodHandle findFactory(Class<? extends T> speciesCode, List<Class<?>> types) { |
|
final MethodType type = baseConstructorType().changeReturnType(topClass()).appendParameterTypes(types); |
|
try { |
|
return IMPL_LOOKUP.findStatic(speciesCode, "make", type); |
|
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) { |
|
throw newInternalError(e); |
|
} |
|
} |
|
} |
|
|
|
|
|
protected Factory makeFactory() { |
|
return new Factory(); |
|
} |
|
|
|
|
|
|
|
private static final String MH = "java/lang/invoke/MethodHandle"; |
|
private static final String MH_SIG = "L" + MH + ";"; |
|
private static final String STABLE = "jdk/internal/vm/annotation/Stable"; |
|
private static final String STABLE_SIG = "L" + STABLE + ";"; |
|
private static final String[] E_THROWABLE = new String[] { "java/lang/Throwable" }; |
|
static { |
|
assert(MH_SIG.equals(classSig(MethodHandle.class))); |
|
assert(MH.equals(classBCName(MethodHandle.class))); |
|
} |
|
|
|
static String methodSig(MethodType mt) { |
|
return mt.toMethodDescriptorString(); |
|
} |
|
static String classSig(Class<?> cls) { |
|
if (cls.isPrimitive() || cls.isArray()) |
|
return MethodType.methodType(cls).toMethodDescriptorString().substring(2); |
|
return classSig(classBCName(cls)); |
|
} |
|
static String classSig(String bcName) { |
|
assert(bcName.indexOf('.') < 0); |
|
assert(!bcName.endsWith(";")); |
|
assert(!bcName.startsWith("[")); |
|
return "L" + bcName + ";"; |
|
} |
|
static String classBCName(Class<?> cls) { |
|
return classBCName(className(cls)); |
|
} |
|
static String classBCName(String str) { |
|
assert(str.indexOf('/') < 0) : str; |
|
return str.replace('.', '/'); |
|
} |
|
static String className(Class<?> cls) { |
|
assert(!cls.isArray() && !cls.isPrimitive()); |
|
return cls.getName(); |
|
} |
|
} |