Skip to content

Commit

Permalink
Added locals and field capturing
Browse files Browse the repository at this point in the history
  • Loading branch information
badlogic committed Jul 17, 2015
1 parent 0c552c1 commit 2d1e086
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ protected Config() throws IOException {
new ObjCMemberPlugin(),
new ObjCBlockPlugin(),
new AnnotationImplPlugin(),
//new LambdaPlugin()
// new LambdaPlugin()
new org.robovm.compiler.plugin.lambda2.LambdaPlugin()
));
this.loadPluginsFromClassPath();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ private void transformMethod(Config config, Clazz clazz, SootClass sootClass,
callSite = altMetafactory(caller, invokedName, invokedType, samMethodType, implMethod,
instantiatedMethodType, bsmArgs);
} else {
new LambdaClassGenerator().generate(sootClass, expr.getMethodRef().name(), expr.getMethodRef(), (SootMethodType)bsmArgs.get(0), (SootMethodHandle)bsmArgs.get(1), (SootMethodType)bsmArgs.get(2));
// new LambdaClassGenerator().generate(sootClass, expr.getMethodRef().name(), expr.getMethodRef(), (SootMethodType)bsmArgs.get(0), (SootMethodHandle)bsmArgs.get(1), (SootMethodType)bsmArgs.get(2));
callSite = LambdaMetafactory.metafactory(caller, invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
/*
* Copyright (C) 2014 RoboVM AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
*/
package org.robovm.compiler.plugin.lambda2;

import soot.SootMethodType;
import soot.Type;

import java.util.List;

import soot.Type;

/**
* Created by badlogic on 09/07/15.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
/*
* Copyright (C) 2014 RoboVM AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
*/
package org.robovm.compiler.plugin.lambda2;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.robovm.compiler.CompilerException;
import org.robovm.compiler.Types;
import soot.*;

Expand Down Expand Up @@ -30,52 +47,83 @@ public LambdaClass generate(SootClass caller, String invokedName, SootMethodRef
null, "java/lang/Object",
new String[]{ functionalInterface });

createConstructor(cw);
createForwardingMethod(cw, invokedType, samMethodType, implMethod, instantiatedMethodType);
String targetMethod = "<init>";
createFieldsAndConstructor(lambdaClassName, cw, invokedType, samMethodType, implMethod, instantiatedMethodType);

// if we perform capturing, we can't cache the
// lambda instance. We need to create a factory method
// that returns a new instance of the lambda
// every time the lambda is invoked. That method
// will be invoked instead of the <init> method
// of the lambda by LambdaPlugin.
if(!invokedType.parameterTypes().isEmpty()) {
targetMethod = createFactory(lambdaClassName, cw, invokedType, samMethodType, implMethod, instantiatedMethodType);
}
createForwardingMethod(lambdaClassName, cw, invokedType, samMethodType, implMethod, instantiatedMethodType);
cw.visitEnd();

return new LambdaClass(lambdaClassName, cw.toByteArray(), "<init>", new ArrayList<Type>(), invokedType.returnType());
return new LambdaClass(lambdaClassName, cw.toByteArray(), targetMethod, invokedType.parameterTypes(), invokedType.returnType());
}

private void createForwardingMethod(ClassWriter cw, SootMethodRef invokedType, SootMethodType samMethodType, SootMethodHandle implMethod, SootMethodType instantiatedMethodType) {
private void createForwardingMethod(String lambdaClassName, ClassWriter cw, SootMethodRef invokedType, SootMethodType samMethodType, SootMethodHandle implMethod, SootMethodType instantiatedMethodType) {
String descriptor = Types.getDescriptor(samMethodType.getParameterTypes(), samMethodType.getReturnType());
String implClassName = implMethod.getMethodRef().declaringClass().getName().replace('.', '/');
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, invokedType.name(), descriptor, null, null);
mv.visitCode();

pushArguments(mv, invokedType, samMethodType, implMethod, instantiatedMethodType);
mv.visitMethodInsn(INVOKESTATIC, implClassName, implMethod.getMethodRef().name(), descriptor, false);
pushArguments(lambdaClassName, mv, invokedType, samMethodType, implMethod, instantiatedMethodType);
int invokeOpCode = INVOKESTATIC;
switch(implMethod.getReferenceKind()) {
case SootMethodHandle.REF_invokeInterface:
invokeOpCode = INVOKEINTERFACE;
break;
case SootMethodHandle.REF_invokeSpecial:
case SootMethodHandle.REF_newInvokeSpecial:
invokeOpCode = INVOKESPECIAL;
break;
case SootMethodHandle.REF_invokeStatic:
invokeOpCode = INVOKESTATIC;
break;
case SootMethodHandle.REF_invokeVirtual:
invokeOpCode = INVOKEVIRTUAL;
break;
default:
throw new CompilerException("Unknown invoke type: " + implMethod.getReferenceKind());
}
String implDescriptor = null;
List<Type> paramTypes = new ArrayList<Type>(implMethod.getMethodType().getParameterTypes());
// need to remove the first parameter (this) in case this
// is an instance method
if(invokeOpCode != INVOKESTATIC && !paramTypes.isEmpty()) {
paramTypes.remove(0);
}
implDescriptor = Types.getDescriptor(paramTypes, implMethod.getMethodType().getReturnType());
mv.visitMethodInsn(invokeOpCode, implClassName, implMethod.getMethodRef().name(), implDescriptor, false);
createForwardingMethodReturn(mv, samMethodType, implMethod, instantiatedMethodType);

mv.visitMaxs(-1, -1);
mv.visitEnd();
}

private void pushArguments(MethodVisitor mv, SootMethodRef invokedType, SootMethodType samMethodType,
private void pushArguments(String lambdaClassName, MethodVisitor mv, SootMethodRef invokedType, SootMethodType samMethodType,
SootMethodHandle implMethod, SootMethodType instantiatedMethodType) {
List<Type> args = samMethodType.getParameterTypes();
int localIndex = 0;
int localIndex = 1; // we start at slot index 1, because this occupies slot 0

// push the captured arguments, may include
// the caller's this if the desugared lambda
// is a instance method
int i = 0;
for(Object obj: invokedType.parameterTypes()) {
Type captureType = (Type)obj;
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, lambdaClassName, "field" + i, Types.getDescriptor(captureType));
i++;
}

for(int i = 0; i < args.size(); i++) {
Type arg = args.get(i);
if(arg instanceof PrimType) {
if(arg.equals(LongType.v())) {
mv.visitVarInsn(LLOAD, localIndex + 1);
} else if(arg.equals(FloatType.v())) {
mv.visitVarInsn(FLOAD, localIndex + 1);
} else if(arg.equals(DoubleType.v())) {
mv.visitVarInsn(DLOAD, localIndex + 1);
} else {
mv.visitVarInsn(ILOAD, localIndex + 1);
}
} else {
mv.visitVarInsn(ALOAD, localIndex + 1);
}
if(arg.equals(LongType.v()) || arg.equals(DoubleType.v())) {
localIndex += 2;
} else {
localIndex += 1;
}
// push the functional interface parameters
for(Type arg: samMethodType.getParameterTypes()) {
mv.visitVarInsn(loadOpcodeForType(arg), localIndex);
localIndex += slotsForType(arg);
}
}

Expand All @@ -98,13 +146,100 @@ private void createForwardingMethodReturn(MethodVisitor mv, SootMethodType samMe
}
}

private void createConstructor(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null);
private void createFieldsAndConstructor(String lambdaClassName, ClassWriter cw, SootMethodRef invokedType, SootMethodType samMethodType, SootMethodHandle implMethod, SootMethodType instantiatedMethodType) {
StringBuffer constructorDescriptor = new StringBuffer();

// create the fields on the class
int i = 0;
for(Object obj: invokedType.parameterTypes()) {
Type captureType = (Type)obj;
String typeDesc = Types.getDescriptor(captureType);
cw.visitField(ACC_PRIVATE + ACC_FINAL, "field" + i, typeDesc, null, null);
constructorDescriptor.append(typeDesc);
i++;
}

// create constructor
MethodVisitor mv = cw.visitMethod(0, "<init>", "(" + constructorDescriptor.toString() + ")V", null, null);
mv.visitCode();

// calls super
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);

// store the captures into the fields
i = 0;
int localIndex = 1; // we start at slot 1, because this occupies slot 0
for(Object obj: invokedType.parameterTypes()) {
Type captureType = (Type)obj;

// load this for put field
mv.visitVarInsn(ALOAD, 0);

// load capture from argument slot
mv.visitVarInsn(loadOpcodeForType(captureType), localIndex);
localIndex += slotsForType(captureType);

// store the capture into the field
mv.visitFieldInsn(PUTFIELD, lambdaClassName, "field" + i, Types.getDescriptor(captureType));

i++;
}

mv.visitInsn(RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
mv.visitEnd();
}

private String createFactory(String lambdaClassName, ClassWriter cw, SootMethodRef invokedType, SootMethodType samMethodType, SootMethodHandle implMethod, SootMethodType instantiatedMethodType) {
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "getLambdaInstance", Types.getDescriptor(invokedType.parameterTypes(), invokedType.returnType()), null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, lambdaClassName);
mv.visitInsn(DUP);
int i = 0;
for(Object obj: invokedType.parameterTypes()) {
Type captureType = (Type)obj;
mv.visitVarInsn(loadOpcodeForType(captureType), i);
i += slotsForType(captureType);
}
mv.visitMethodInsn(INVOKESPECIAL, lambdaClassName, "<init>", Types.getDescriptor(invokedType.parameterTypes(), VoidType.v()), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
return "getLambdaInstance";
}

public int loadOpcodeForType(Type type) {
if(type instanceof PrimType) {
if(type.equals(LongType.v())) {
return LLOAD;
} else if(type.equals(FloatType.v())) {
return FLOAD;
} else if(type.equals(DoubleType.v())) {
return DLOAD;
} else {
return ILOAD;
}
} else {
return ALOAD;
}
}

public int slotsForType(Type type) {
if(type.equals(LongType.v()) || type.equals(DoubleType.v())) {
return 2;
} else {
return 1;
}
}

public static class A {
final LambdaClassGenerator gen;
final int a;

public A(LambdaClassGenerator get, int a) {
this.gen = get;
this.a = a;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright (C) 2014 RoboVM AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
*/
package org.robovm.compiler.plugin.lambda2;

import org.apache.commons.io.FileUtils;
Expand Down Expand Up @@ -109,7 +125,13 @@ private void transformMethod(Config config, Clazz clazz, SootClass sootClass,
newUnits.add(Jimple.v().newAssignStmt(l, tmp));
} else {
// Static factory method returns the lambda to use.
// TODO: implement altMetaFactory behaviour
newUnits.add(Jimple.v().newAssignStmt(l,
Jimple.v().newStaticInvokeExpr(
Scene.v().makeMethodRef(lambdaClass,
callSite.getTargetMethodName(),
callSite.getTargetMethodParameters(),
samType, true),
expr.getArgs())));
}
units.insertAfter(newUnits, unit);
units.remove(unit);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.robovm.rt.lambdas.test001;

import static org.junit.Assert.*;
Expand Down
21 changes: 0 additions & 21 deletions tests/robovm/src/test/java/org/robovm/rt/lambdas/test003/I.java

This file was deleted.

0 comments on commit 2d1e086

Please sign in to comment.