Skip to content
This repository has been archived by the owner on Sep 28, 2022. It is now read-only.

Commit

Permalink
Named parameters support for MethodInvocation
Browse files Browse the repository at this point in the history
  • Loading branch information
danielpetisme committed May 7, 2015
1 parent 6832f2b commit 5a82c91
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 23 deletions.
Expand Up @@ -59,7 +59,7 @@ class JavaBytecodeGenerationGoloIrVisitor implements GoloIrVisitor {

bootstrapOwner = "fr/insalyon/citi/golo/runtime/MethodInvocationSupport";
bootstrapMethod = "bootstrap";
description = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/CallSite;";
description = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;";
METHOD_INVOCATION_HANDLE = new Handle(H_INVOKESTATIC, bootstrapOwner, bootstrapMethod, description);

bootstrapOwner = "fr/insalyon/citi/golo/runtime/ClassReferenceSupport";
Expand Down Expand Up @@ -463,7 +463,7 @@ public void visitThrowStatement(ThrowStatement throwStatement) {
private List<String> visitInvocationArguments(AbstractInvocation invocation) {
List<String> argumentNames = new ArrayList<>();
for (ExpressionStatement argument : invocation.getArguments()) {
if (invocation instanceof FunctionInvocation && ((FunctionInvocation) invocation).usesNamedArguments()) {
if (invocation.usesNamedArguments()) {
NamedArgument namedArgument = (NamedArgument) argument;
argumentNames.add(namedArgument.getName());
argument = namedArgument.getExpression();
Expand Down Expand Up @@ -503,12 +503,15 @@ public void visitFunctionInvocation(FunctionInvocation functionInvocation) {

@Override
public void visitMethodInvocation(MethodInvocation methodInvocation) {
visitInvocationArguments(methodInvocation);
List<Object> bootstrapArgs = new ArrayList<>();
bootstrapArgs.add(methodInvocation.isNullSafeGuarded() ? 1 : 0);
List<String> argumentNames = visitInvocationArguments(methodInvocation);
bootstrapArgs.addAll(argumentNames);
methodVisitor.visitInvokeDynamicInsn(
methodInvocation.getName().replaceAll("\\.", "#"),
goloFunctionSignature(methodInvocation.getArity() + 1),
METHOD_INVOCATION_HANDLE,
(Boolean) methodInvocation.isNullSafeGuarded());
bootstrapArgs.toArray());
for (FunctionInvocation invocation : methodInvocation.getAnonymousFunctionInvocations()) {
invocation.accept(this);
}
Expand Down
Expand Up @@ -731,12 +731,23 @@ public Object visit(ASTMethodInvocation node, Object data) {
int i = 0;
final int numChildren = node.jjtGetNumChildren();
for (i = 0; i < numChildren; i++) {
GoloASTNode parameterNode = (GoloASTNode) node.jjtGetChild(i);
if (parameterNode instanceof ASTAnonymousFunctionInvocation) {
GoloASTNode argumentNode = (GoloASTNode) node.jjtGetChild(i);
if (argumentNode instanceof ASTAnonymousFunctionInvocation) {
break;
}
parameterNode.jjtAccept(this, data);
methodInvocation.addArgument((ExpressionStatement) context.objectStack.pop());
argumentNode.jjtAccept(this, data);
ExpressionStatement statement = (ExpressionStatement) context.objectStack.pop();
if (statement instanceof NamedArgument) {
if (!methodInvocation.getArguments().isEmpty() && !methodInvocation.usesNamedArguments()) {
getOrCreateExceptionBuilder(context).report(INCOMPLETE_NAMED_ARGUMENTS_USAGE, node,
"Function `" + node.getName() + "` invocation should name either all or none of its arguments" +
" at (line=" + node.getLineInSourceCode() +
", column=" + node.getColumnInSourceCode() + ")"
);
}
methodInvocation.setUsesNamedArguments(true);
}
methodInvocation.addArgument(statement);
}
context.objectStack.push(methodInvocation);
node.setIrElement(methodInvocation);
Expand Down
Expand Up @@ -25,6 +25,7 @@ public abstract class AbstractInvocation extends ExpressionStatement {
private final String name;
private final List<ExpressionStatement> arguments = new LinkedList<>();
private final List<FunctionInvocation> anonymousFunctionInvocations = new LinkedList<>();
protected boolean usesNamedArguments = false;

public AbstractInvocation(String name) {
super();
Expand Down Expand Up @@ -54,4 +55,12 @@ public void addAnonymousFunctionInvocation(FunctionInvocation invocation) {
public List<FunctionInvocation> getAnonymousFunctionInvocations() {
return Collections.unmodifiableList(anonymousFunctionInvocations);
}

public boolean usesNamedArguments() {
return usesNamedArguments;
}

public void setUsesNamedArguments(boolean usesNamedArguments) {
this.usesNamedArguments = usesNamedArguments;
}
}
Expand Up @@ -22,7 +22,6 @@ public class FunctionInvocation extends AbstractInvocation {
private boolean onModuleState = false;
private boolean anonymous = false;
private boolean constant = false;
private boolean usesNamedArguments = false;

public FunctionInvocation() {
super("anonymous");
Expand Down Expand Up @@ -61,14 +60,6 @@ public void setConstant(boolean constant) {
this.constant = constant;
}

public boolean usesNamedArguments() {
return usesNamedArguments;
}

public void setUsesNamedArguments(boolean usesNamedArguments) {
this.usesNamedArguments = usesNamedArguments;
}

@Override
public void accept(GoloIrVisitor visitor) {
visitor.visitFunctionInvocation(this);
Expand Down
Expand Up @@ -40,6 +40,7 @@ class AugmentationMethodFinder implements MethodFinder {
new ExternalFQNAugmentationStrategy(),
new ImportedExternalNamedAugmentationStrategy()
};
private final String[] argumentNames;

public AugmentationMethodFinder(MethodInvocationSupport.InlineCache inlineCache, Class<?> receiverClass, Object[] args) {
this.receiverClass = receiverClass;
Expand All @@ -51,6 +52,9 @@ public AugmentationMethodFinder(MethodInvocationSupport.InlineCache inlineCache,
this.callerClass = inlineCache.callerLookup.lookupClass();
this.classLoader = callerClass.getClassLoader();
this.methodHandle = null;
this.argumentNames = new String[inlineCache.argumentNames.length + 1];
this.argumentNames[0] = "this";
System.arraycopy(inlineCache.argumentNames,0, argumentNames, 1, inlineCache.argumentNames.length);
}


Expand Down Expand Up @@ -172,6 +176,9 @@ private boolean matchesArity(Method method) {
private MethodHandle toMethodHandle(Method method) {
try {
MethodHandle target = lookup.unreflect(method);
if(argumentNames.length > 1) {
target = FunctionCallSupport.reorderArguments(method, target, argumentNames);
}
if (target.isVarargsCollector() && isLastArgumentAnArray(arity, args)) {
return target.asFixedArity().asType(type);
} else {
Expand Down
Expand Up @@ -190,7 +190,7 @@ public static Object fallback(FunctionCallSite callSite, Object[] args) throws T
}
}

private static MethodHandle reorderArguments(Method method, MethodHandle handle, String[] argumentNames) {
public static MethodHandle reorderArguments(Method method, MethodHandle handle, String[] argumentNames) {
if (Arrays.stream(method.getParameters()).allMatch(p -> p.isNamePresent())) {
String[] parameterNames = Arrays.stream(method.getParameters()).map(Parameter::getName).toArray(String[]::new);
int[] argumentsOrder = new int[parameterNames.length];
Expand Down
Expand Up @@ -39,15 +39,18 @@ static final class InlineCache extends MutableCallSite {
final MethodHandles.Lookup callerLookup;
final String name;
final boolean nullSafeGuarded;
final String[] argumentNames;


int depth = 0;
WeakHashMap<Class, MethodHandle> vtable;

InlineCache(Lookup callerLookup, String name, MethodType type, boolean nullSafeGuarded) {
InlineCache(Lookup callerLookup, String name, MethodType type, boolean nullSafeGuarded, String... argumentNames) {
super(type);
this.callerLookup = callerLookup;
this.name = name;
this.nullSafeGuarded = nullSafeGuarded;
this.argumentNames = argumentNames;
}

boolean isMegaMorphic() {
Expand Down Expand Up @@ -98,8 +101,13 @@ boolean isMegaMorphic() {
}
}

public static CallSite bootstrap(Lookup caller, String name, MethodType type, int nullSafeGuarded) {
InlineCache callSite = new InlineCache(caller, name, type, (nullSafeGuarded != 0));
public static CallSite bootstrap(Lookup caller, String name, MethodType type, Object... bsmArgs) {
boolean nullSafeGuarded = ((int)bsmArgs[0]) == 1;
String[] argumentNames = new String[bsmArgs.length - 1];
for (int i = 0; i < bsmArgs.length -1; i++) {
argumentNames[i] = (String) bsmArgs[i+1];
}
InlineCache callSite = new InlineCache(caller, name, type, nullSafeGuarded, argumentNames);
MethodHandle fallbackHandle = FALLBACK
.bindTo(callSite)
.asCollector(Object[].class, type.parameterCount())
Expand Down
Expand Up @@ -36,6 +36,7 @@ class RegularMethodFinder implements MethodFinder {
private final Lookup lookup;
private final boolean makeAccessible;
private final int arity;
private final String[] argumentNames;

public RegularMethodFinder(MethodInvocationSupport.InlineCache inlineCache, Class<?> receiverClass, Object[] args) {
this.args = args;
Expand All @@ -45,6 +46,9 @@ public RegularMethodFinder(MethodInvocationSupport.InlineCache inlineCache, Clas
this.lookup = inlineCache.callerLookup;
this.makeAccessible = !isPublic(receiverClass.getModifiers());
this.arity = type.parameterArray().length;
this.argumentNames = new String[inlineCache.argumentNames.length + 1];
this.argumentNames[0] = "this";
System.arraycopy(inlineCache.argumentNames,0, argumentNames, 1, inlineCache.argumentNames.length);
}

@Override
Expand Down Expand Up @@ -87,6 +91,9 @@ private MethodHandle toMethodHandle(Method method) throws IllegalAccessException
} else {
target = lookup.unreflect(method).asType(type);
}
if(argumentNames.length > 1) {
target = FunctionCallSupport.reorderArguments(method, target, argumentNames);
}
return FunctionCallSupport.insertSAMFilter(target, lookup, method.getParameterTypes(), 1);
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/jjtree/Golo.jjt
Expand Up @@ -1098,7 +1098,7 @@ void MethodInvocation():
Token token;
}
{
token=<IDENTIFIER> "(" (BlankLine())? (Expressions())? ")"
token=<IDENTIFIER> "(" (BlankLine())? (Arguments())? ")"
(LOOKAHEAD(2) AnonymousFunctionInvocation())*
{
jjtThis.setName(token.image);
Expand Down
Expand Up @@ -1771,5 +1771,13 @@ public void test_named_parameters() throws Throwable {
Problem problem = e.getProblems().get(0);
assertThat(problem.getType(), is(INCOMPLETE_NAMED_ARGUMENTS_USAGE));
}

Method golo_decoratored = moduleClass.getMethod("golo_decoratored");
result = (String) golo_decoratored.invoke(null);
assertThat(result, is("<Golo>"));

Method golo_augmentation_varargs = moduleClass.getMethod("golo_augmentation_varargs");
result = (String) golo_augmentation_varargs.invoke(null);
assertThat(result, is("abc"));
}
}
Expand Up @@ -18,4 +18,13 @@ function joiner = |delimiter, values...| {
return joined: toString()
}

function csv_builder = -> joiner(values = array["a", "b", "c"], delimiter = ",")
function csv_builder = -> joiner(values = array["a", "b", "c"], delimiter = ",")

augment java.lang.String {
function decorate = |this, prefix, suffix| -> prefix + this + suffix
function append = |this, values...| -> joiner("", values)
}

function golo_decoratored = -> "Golo": decorate(suffix = ">", prefix = "<")

function golo_augmentation_varargs = -> "": append(values = array["a", "b", "c"])
Expand Up @@ -5,6 +5,9 @@ augment java.lang.String {
function append = |this, tail| -> this + tail

function toURL = |this| -> java.net.URL(this)

}

function id = |x| -> x

function named_append = -> "foo": append(tail = "/")

0 comments on commit 5a82c91

Please sign in to comment.