Skip to content

Commit

Permalink
Merge pull request #19003 from rmuir/augmentation
Browse files Browse the repository at this point in the history
painless: add augmentation
  • Loading branch information
rmuir authored Jun 21, 2016
2 parents 969e953 + 1b9695a commit f70211d
Show file tree
Hide file tree
Showing 20 changed files with 776 additions and 105 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -350,10 +350,10 @@ private static MethodHandle lookupReferenceInternal(Lookup lookup, Definition.Ty
}
throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");
}
ref = new FunctionRef(clazz, interfaceMethod, handle, captures);
ref = new FunctionRef(clazz, interfaceMethod, handle, captures.length);
} else {
// whitelist lookup
ref = new FunctionRef(clazz, type, call, captures);
ref = new FunctionRef(clazz, type, call, captures.length);
}
final CallSite callSite;
if (ref.needsBridges()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,17 @@ public String toString() {
public static class Method {
public final String name;
public final Struct owner;
public final boolean augmentation;
public final Type rtn;
public final List<Type> arguments;
public final org.objectweb.asm.commons.Method method;
public final int modifiers;
public final MethodHandle handle;

public Method(String name, Struct owner, Type rtn, List<Type> arguments,
public Method(String name, Struct owner, boolean augmentation, Type rtn, List<Type> arguments,
org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
this.name = name;
this.augmentation = augmentation;
this.owner = owner;
this.rtn = rtn;
this.arguments = Collections.unmodifiableList(arguments);
Expand All @@ -217,7 +219,15 @@ public MethodType getMethodType() {
// otherwise compute it
final Class<?> params[];
final Class<?> returnValue;
if (Modifier.isStatic(modifiers)) {
if (augmentation) {
// static method disguised as virtual/interface method
params = new Class<?>[1 + arguments.size()];
params[0] = Augmentation.class;
for (int i = 0; i < arguments.size(); i++) {
params[i + 1] = arguments.get(i).clazz;
}
returnValue = rtn.clazz;
} else if (Modifier.isStatic(modifiers)) {
// static method: straightforward copy
params = new Class<?>[arguments.size()];
for (int i = 0; i < arguments.size(); i++) {
Expand All @@ -242,6 +252,24 @@ public MethodType getMethodType() {
}
return MethodType.methodType(returnValue, params);
}

public void write(MethodWriter writer) {
final org.objectweb.asm.Type type;
if (augmentation) {
assert java.lang.reflect.Modifier.isStatic(modifiers);
type = WriterConstants.AUGMENTATION_TYPE;
} else {
type = owner.type;
}

if (java.lang.reflect.Modifier.isStatic(modifiers)) {
writer.invokeStatic(type, method);
} else if (java.lang.reflect.Modifier.isInterface(owner.clazz.getModifiers())) {
writer.invokeInterface(type, method);
} else {
writer.invokeVirtual(type, method);
}
}
}

public static final class Field {
Expand Down Expand Up @@ -690,7 +718,7 @@ private final void addConstructorInternal(final String struct, final String name
" with arguments " + Arrays.toString(classes) + ".");
}

final Method constructor = new Method(name, owner, returnType, Arrays.asList(args), asm, reflect.getModifiers(), handle);
final Method constructor = new Method(name, owner, false, returnType, Arrays.asList(args), asm, reflect.getModifiers(), handle);

owner.constructors.put(methodKey, constructor);
}
Expand Down Expand Up @@ -734,24 +762,20 @@ private final void addSignature(String className, String signature) {
}
addConstructorInternal(className, "<init>", args);
} else {
if (methodName.indexOf('/') >= 0) {
String nameAndAlias[] = methodName.split("/");
if (nameAndAlias.length != 2) {
throw new IllegalArgumentException("Currently only two aliases are allowed!");
}
addMethodInternal(className, nameAndAlias[0], nameAndAlias[1], rtn, args);
if (methodName.indexOf("*") >= 0) {
addMethodInternal(className, methodName.substring(0, methodName.length() - 1), true, rtn, args);
} else {
addMethodInternal(className, methodName, null, rtn, args);
addMethodInternal(className, methodName, false, rtn, args);
}
}
} else {
// field
addFieldInternal(className, elements[1], null, rtn);
addFieldInternal(className, elements[1], rtn);
}
}

private final void addMethodInternal(final String struct, final String name, final String alias,
final Type rtn, final Type[] args) {
private final void addMethodInternal(String struct, String name, boolean augmentation,
Type rtn, Type[] args) {
final Struct owner = structsMap.get(struct);

if (owner == null) {
Expand All @@ -777,20 +801,32 @@ private final void addMethodInternal(final String struct, final String name, fin
"Duplicate method signature [" + methodKey + "] found within the struct [" + owner.name + "].");
}

final Class<?>[] classes = new Class<?>[args.length];

for (int count = 0; count < classes.length; ++count) {
classes[count] = args[count].clazz;
final Class<?> implClass;
final Class<?>[] params;

if (augmentation == false) {
implClass = owner.clazz;
params = new Class<?>[args.length];
for (int count = 0; count < args.length; ++count) {
params[count] = args[count].clazz;
}
} else {
implClass = Augmentation.class;
params = new Class<?>[args.length + 1];
params[0] = owner.clazz;
for (int count = 0; count < args.length; ++count) {
params[count+1] = args[count].clazz;
}
}

final java.lang.reflect.Method reflect;

try {
reflect = owner.clazz.getMethod(alias == null ? name : alias, classes);
} catch (final NoSuchMethodException exception) {
throw new IllegalArgumentException("Method [" + (alias == null ? name : alias) +
"] not found for class [" + owner.clazz.getName() + "]" +
" with arguments " + Arrays.toString(classes) + ".");
reflect = implClass.getMethod(name, params);
} catch (NoSuchMethodException exception) {
throw new IllegalArgumentException("Method [" + name +
"] not found for class [" + implClass.getName() + "]" +
" with arguments " + Arrays.toString(params) + ".");
}

if (!reflect.getReturnType().equals(rtn.clazz)) {
Expand All @@ -805,25 +841,24 @@ private final void addMethodInternal(final String struct, final String name, fin
MethodHandle handle;

try {
handle = MethodHandles.publicLookup().in(owner.clazz).unreflect(reflect);
handle = MethodHandles.publicLookup().in(implClass).unreflect(reflect);
} catch (final IllegalAccessException exception) {
throw new IllegalArgumentException("Method [" + (alias == null ? name : alias) + "]" +
" not found for class [" + owner.clazz.getName() + "]" +
" with arguments " + Arrays.toString(classes) + ".");
throw new IllegalArgumentException("Method [" + name + "]" +
" not found for class [" + implClass.getName() + "]" +
" with arguments " + Arrays.toString(params) + ".");
}

final int modifiers = reflect.getModifiers();
final Method method = new Method(name, owner, rtn, Arrays.asList(args), asm, modifiers, handle);
final Method method = new Method(name, owner, augmentation, rtn, Arrays.asList(args), asm, modifiers, handle);

if (java.lang.reflect.Modifier.isStatic(modifiers)) {
if (augmentation == false && java.lang.reflect.Modifier.isStatic(modifiers)) {
owner.staticMethods.put(methodKey, method);
} else {
owner.methods.put(methodKey, method);
}
}

private final void addFieldInternal(final String struct, final String name, final String alias,
final Type type) {
private final void addFieldInternal(String struct, String name, Type type) {
final Struct owner = structsMap.get(struct);

if (owner == null) {
Expand All @@ -844,9 +879,9 @@ private final void addFieldInternal(final String struct, final String name, fina
java.lang.reflect.Field reflect;

try {
reflect = owner.clazz.getField(alias == null ? name : alias);
reflect = owner.clazz.getField(name);
} catch (final NoSuchFieldException exception) {
throw new IllegalArgumentException("Field [" + (alias == null ? name : alias) + "]" +
throw new IllegalArgumentException("Field [" + name + "]" +
" not found for class [" + owner.clazz.getName() + "].");
}

Expand All @@ -862,7 +897,7 @@ private final void addFieldInternal(final String struct, final String name, fina
setter = MethodHandles.publicLookup().unreflectSetter(reflect);
}
} catch (final IllegalAccessException exception) {
throw new IllegalArgumentException("Getter/Setter [" + (alias == null ? name : alias) + "]" +
throw new IllegalArgumentException("Getter/Setter [" + name + "]" +
" not found for class [" + owner.clazz.getName() + "].");
}

Expand All @@ -875,9 +910,9 @@ private final void addFieldInternal(final String struct, final String name, fina
" within the struct [" + owner.name + "] is not final.");
}

owner.staticMembers.put(alias == null ? name : alias, field);
owner.staticMembers.put(name, field);
} else {
owner.members.put(alias == null ? name : alias, field);
owner.members.put(name, field);
}
}

Expand Down Expand Up @@ -915,11 +950,24 @@ private void copyStruct(String struct, List<String> children) {
// https://bugs.openjdk.java.net/browse/JDK-8072746
} else {
try {
Class<?> arguments[] = new Class<?>[method.arguments.size()];
for (int i = 0; i < method.arguments.size(); i++) {
arguments[i] = method.arguments.get(i).clazz;
// TODO: we *have* to remove all these public members and use getter methods to encapsulate!
final Class<?> impl;
final Class<?> arguments[];
if (method.augmentation) {
impl = Augmentation.class;
arguments = new Class<?>[method.arguments.size() + 1];
arguments[0] = method.owner.clazz;
for (int i = 0; i < method.arguments.size(); i++) {
arguments[i + 1] = method.arguments.get(i).clazz;
}
} else {
impl = owner.clazz;
arguments = new Class<?>[method.arguments.size()];
for (int i = 0; i < method.arguments.size(); i++) {
arguments[i] = method.arguments.get(i).clazz;
}
}
java.lang.reflect.Method m = owner.clazz.getMethod(method.method.getName(), arguments);
java.lang.reflect.Method m = impl.getMethod(method.method.getName(), arguments);
if (m.getReturnType() != method.rtn.clazz) {
throw new IllegalStateException("missing covariant override for: " + m + " in " + owner.name);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,24 +53,27 @@ public class FunctionRef {
* @param expected interface type to implement.
* @param type the left hand side of a method reference expression
* @param call the right hand side of a method reference expression
* @param captures captured arguments
* @param numCaptures number of captured arguments
*/
public FunctionRef(Definition.Type expected, String type, String call, Class<?>... captures) {
this(expected, expected.struct.getFunctionalMethod(), lookup(expected, type, call, captures.length > 0), captures);
public FunctionRef(Definition.Type expected, String type, String call, int numCaptures) {
this(expected, expected.struct.getFunctionalMethod(), lookup(expected, type, call, numCaptures > 0), numCaptures);
}

/**
* Creates a new FunctionRef (already resolved)
* @param expected interface type to implement
* @param method functional interface method
* @param impl implementation method
* @param captures captured arguments
* @param numCaptures number of captured arguments
*/
public FunctionRef(Definition.Type expected, Definition.Method method, Definition.Method impl, Class<?>... captures) {
public FunctionRef(Definition.Type expected, Definition.Method method, Definition.Method impl, int numCaptures) {
// e.g. compareTo
invokedName = method.name;
// e.g. (Object)Comparator
invokedType = MethodType.methodType(expected.clazz, captures);
MethodType implType = impl.getMethodType();
// only include captured parameters as arguments
invokedType = MethodType.methodType(expected.clazz,
implType.dropParameterTypes(numCaptures, implType.parameterCount()));
// e.g. (Object,Object)int
interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1);

Expand All @@ -90,6 +93,9 @@ public FunctionRef(Definition.Type expected, Definition.Method method, Definitio
// owner == null: script class itself
ownerIsInterface = false;
owner = WriterConstants.CLASS_TYPE.getInternalName();
} else if (impl.augmentation) {
ownerIsInterface = false;
owner = WriterConstants.AUGMENTATION_TYPE.getInternalName();
} else {
ownerIsInterface = impl.owner.clazz.isInterface();
owner = impl.owner.type.getInternalName();
Expand All @@ -98,19 +104,22 @@ public FunctionRef(Definition.Type expected, Definition.Method method, Definitio
implMethod = impl.handle;

// remove any prepended captured arguments for the 'natural' signature.
samMethodType = adapt(interfaceMethodType, impl.getMethodType().dropParameterTypes(0, captures.length));
samMethodType = adapt(interfaceMethodType, impl.getMethodType().dropParameterTypes(0, numCaptures));
}

/**
* Creates a new FunctionRef (low level).
* <p>
* This will <b>not</b> set implMethodASM. It is for runtime use only.
*/
public FunctionRef(Definition.Type expected, Definition.Method method, MethodHandle impl, Class<?>... captures) {
public FunctionRef(Definition.Type expected, Definition.Method method, MethodHandle impl, int numCaptures) {
// e.g. compareTo
invokedName = method.name;
// e.g. (Object)Comparator
invokedType = MethodType.methodType(expected.clazz, captures);
MethodType implType = impl.type();
// only include captured parameters as arguments
invokedType = MethodType.methodType(expected.clazz,
implType.dropParameterTypes(numCaptures, implType.parameterCount()));
// e.g. (Object,Object)int
interfaceMethodType = method.getMethodType().dropParameterTypes(0, 1);

Expand All @@ -119,7 +128,7 @@ public FunctionRef(Definition.Type expected, Definition.Method method, MethodHan
implMethodASM = null;

// remove any prepended captured arguments for the 'natural' signature.
samMethodType = adapt(interfaceMethodType, impl.type().dropParameterTypes(0, captures.length));
samMethodType = adapt(interfaceMethodType, impl.type().dropParameterTypes(0, numCaptures));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public final class WriterConstants {
public final static Method CHAR_TO_STRING = getAsmMethod(String.class, "charToString", char.class);

public final static Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class);

public static final Type AUGMENTATION_TYPE = Type.getType(Augmentation.class);

/**
* A Method instance for {@linkplain Pattern#compile}. This isn't available from Definition because we intentionally don't add it there
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void analyze(Locals variables) {
// static case
if (captured.type.sort != Definition.Sort.DEF) {
try {
ref = new FunctionRef(expected, captured.type.name, call, captured.type.clazz);
ref = new FunctionRef(expected, captured.type.name, call, 1);
} catch (IllegalArgumentException e) {
throw createError(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ void analyze(Locals locals) {
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " +
"to [" + expected.name + "], function not found");
}
ref = new FunctionRef(expected, interfaceMethod, implMethod);
ref = new FunctionRef(expected, interfaceMethod, implMethod, 0);
} else {
// whitelist lookup
ref = new FunctionRef(expected, type, call);
ref = new FunctionRef(expected, type, call, 0);
}
} catch (IllegalArgumentException e) {
throw createError(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,7 @@ void analyze(Locals locals) {
} else {
defPointer = null;
try {
Class<?> captureClasses[] = new Class<?>[captures.size()];
for (int i = 0; i < captures.size(); i++) {
captureClasses[i] = captures.get(i).type.clazz;
}
ref = new FunctionRef(expected, interfaceMethod, desugared.method, captureClasses);
ref = new FunctionRef(expected, interfaceMethod, desugared.method, captures.size());
} catch (IllegalArgumentException e) {
throw createError(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,8 @@ void load(MethodWriter writer, Globals globals) {
for (AExpression argument : arguments) {
argument.write(writer, globals);
}

if (java.lang.reflect.Modifier.isStatic(method.modifiers)) {
writer.invokeStatic(method.owner.type, method.method);
} else if (java.lang.reflect.Modifier.isInterface(method.owner.clazz.getModifiers())) {
writer.invokeInterface(method.owner.type, method.method);
} else {
writer.invokeVirtual(method.owner.type, method.method);
}

method.write(writer);
}

@Override
Expand Down
Loading

0 comments on commit f70211d

Please sign in to comment.