Commit 294bf36d authored by Lubomir Bulej's avatar Lubomir Bulej

- Add support to tolerate lambda classes. These do not come with bytecode,

  so for now, we just have a special ShadowClass variant to avoid crashes.
  Getting complete class information will probably require using reflection
  at the agent side, but let's postpone it until it is really needed.
- Add a battery of simple tests to check basic sanity of LambdaShadowClass.
- Add a simple ShadowVM test to check that we can pass a lambda object to REDispatch.
- Rework class name generation in ShadowClass and PrimitiveShadowClass to
  avoid calling overridable public methods.
parent b15502be
package ch.usi.dag.dislreserver.shadow;
import java.util.stream.Stream;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
final class LambdaShadowClass extends ShadowClass {
private final ShadowClass __superClass;
//
LambdaShadowClass (
final long netReference, final Type type,
final ShadowObject classLoader, final ShadowClass superClass
) {
super (netReference, type, classLoader);
__superClass = superClass;
}
//
@Override
public String getName () {
return __canonicalName ();
}
@Override
public String getSimpleName () {
return _simpleName (__canonicalName ());
}
@Override
public String getCanonicalName () {
return __canonicalName ();
}
private String __canonicalName () {
//
// Avoid Type.getClassName() because it converts all slashes to dots,
// instead of only those preceding the $$Lambda$ suffix. Also, the dollars
// in the lambda type canonical names should not be converted to dots.
//
final String name = _type ().getInternalName ();
final int start = name.lastIndexOf ("$$Lambda$");
assert start > 0;
return _javaName (name.substring (0, start)).concat (name.substring (start));
}
//
/**
* @see Class#isInstance(Object)
*/
@Override
public boolean isInstance (final ShadowObject object) {
return equals (object.getShadowClass ());
}
/**
* @see Class#isAssignableFrom(Class)
*/
@Override
public boolean isAssignableFrom (final ShadowClass other) {
return this.equals (other);
}
//
@Override
public int getModifiers () {
//
// Lambda classes are SYNTHETIC and FINAL.
//
return Opcodes.ACC_SYNTHETIC | Opcodes.ACC_FINAL;
}
//
@Override
public ShadowClass getSuperclass () {
// Lambda types have Object as the super class.
return __superClass;
}
@Override
public ShadowClass [] getInterfaces () {
throw new UnsupportedOperationException ("not yet implemented");
}
@Override
public String [] getInterfaceDescriptors () {
throw new UnsupportedOperationException ("not yet implemented");
}
//
@Override
protected Stream <FieldInfo> _declaredFields () {
// Lambda types have no declared fields.
return Stream.empty ();
}
@Override
protected Stream <MethodInfo> _declaredMethods () {
throw new UnsupportedOperationException ("not yet implemented");
}
}
......@@ -51,7 +51,7 @@ final class PrimitiveShadowClass extends ShadowClass {
@Override
public String getName () {
// Avoid Type.getInternalName() -- returns null for primitive types.
return getCanonicalName ();
return _type ().getClassName ();
}
//
......
......@@ -120,22 +120,35 @@ public abstract class ShadowClass extends ShadowObject {
//
public String getName () {
return __type.getInternalName ().replace ('/', '.');
//
// Avoid Type.getClassName() because it adds "class" prefix to array types.
//
return _javaName (__type.getInternalName ());
}
protected static String _javaName (final String name) {
return name.replace ('/', '.');
}
public String getSimpleName () {
return __simpleName (getCanonicalName ());
return _simpleName (__canonicalName ());
}
private static String __simpleName (final String name) {
protected static String _simpleName (final String name) {
// If '.' is not found, index is -1 => +1 adjustment gives index 0
return name.substring (name.lastIndexOf ('.') + 1);
}
public String getCanonicalName () {
return __canonicalName ();
}
private String __canonicalName () {
return __type.getClassName ().replace ('$', '.');
}
......
......@@ -161,7 +161,20 @@ public final class ShadowClassTable {
final byte [] classCode = classTypeMap.get (type);
if (classCode == null || classCode.length == 0) {
throw new DiSLREServerFatalException ("No bytecode found for "+ type);
//
// Lambda classes have no bytecode. Create a dummy shadow class
// for them until we need to figure out how to get the information
// about the class (i.e. what interface it implements) to the
// Shadow VM.
//
if (type.getInternalName ().contains ("$$Lambda$")) {
return new LambdaShadowClass (
netReference, type, classLoader, superClass
);
} else {
throw new DiSLREServerFatalException ("No bytecode found for "+ type + classCode);
}
}
return new ObjectShadowClass (
......
package ch.usi.dag.disl.test.suite.dispatchlambda.app;
import java.util.Comparator;
import java.util.function.IntFunction;
import java.util.stream.IntStream;
import ch.usi.dag.dislre.REDispatch;
public class TargetClass {
private static short __lambdaEventId__ = REDispatch.registerMethod (
"ch.usi.dag.disl.test.suite.dispatchlambda.instr.Analysis.lambdaEvent"
);
static void sendLambda (final int index, final Object object) {
REDispatch.analysisStart (__lambdaEventId__);
REDispatch.sendInt (index);
REDispatch.sendObject (object);
REDispatch.analysisEnd ();
}
//
public static void main (final String [] args) throws InterruptedException {
IntStream.range (0, 10).forEach (i ->
sendLambda (i, (Comparator <String>) String::compareTo)
);
IntStream.range (10, 20).forEach (i -> {
sendLambda (i, (IntFunction <String>) Integer::toString);
});
}
}
package ch.usi.dag.disl.test.suite.dispatchlambda.instr;
import java.util.concurrent.atomic.AtomicLong;
import ch.usi.dag.dislreserver.remoteanalysis.RemoteAnalysis;
import ch.usi.dag.dislreserver.shadow.ShadowObject;
public class Analysis extends RemoteAnalysis {
private final AtomicLong __lambdaEventsTotal = new AtomicLong ();
//
public void lambdaEvent (final int index, final ShadowObject object) {
final String name = object.getShadowClass ().getName ();
System.out.println (index +": "+ name.substring (0, name.lastIndexOf ("/")));
__lambdaEventsTotal.incrementAndGet ();
}
@Override
public void atExit () {
System.out.println ("Total number of lambda events: " + __lambdaEventsTotal);
}
@Override
public void objectFree (final ShadowObject netRef) {
// do nothing
}
}
package ch.usi.dag.disl.test.suite.dispatchlambda.junit;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import ch.usi.dag.disl.test.suite.ShadowVmOnlyTest;
@RunWith (JUnit4.class)
public class DispatchLambdaTest extends ShadowVmOnlyTest {
}
0: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
1: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
2: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
3: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
4: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
5: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
6: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
7: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
8: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
9: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$2
10: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
11: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
12: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
13: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
14: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
15: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
16: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
17: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
18: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
19: ch.usi.dag.disl.test.suite.dispatchlambda.app.TargetClass$$Lambda$4
Total number of lambda events: 20
package ch.usi.dag.dislreserver.shadow;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.IntFunction;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.experimental.theories.ParameterSignature;
import org.junit.experimental.theories.ParameterSupplier;
import org.junit.experimental.theories.ParametersSuppliedBy;
import org.junit.experimental.theories.PotentialAssignment;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import org.objectweb.asm.Type;
@RunWith (Theories.class)
public class LambdaShadowClassTest extends ShadowClassTestBase {
private static final AtomicLong __uniqueId__ = new AtomicLong (1);
private static final HashMap <Class <?>, ShadowClass> __classCache__ = new HashMap <> ();
//
public static class LambdaTypeSupplier extends ParameterSupplier {
@Override
public List <PotentialAssignment> getValueSources (final ParameterSignature sig) {
return _createClassAssignments (new Class <?> [] {
((IntFunction <String>) Integer::toString).getClass (),
((Comparator <String>) String::compareTo).getClass (),
});
}
}
//
@Override
protected ShadowClass _createShadowClass (final Class <?> type) {
Assert.assertTrue (!type.isPrimitive () && !type.isArray ());
final ShadowClass result = __classCache__.get (type);
if (result != null) {
return result;
}
final Class <?> superclass = type.getSuperclass ();
if (superclass != null) {
return __newShadowClass (type, _createShadowClass (superclass));
}
return __newShadowClass (type, null);
}
private static ShadowClass __newShadowClass (
final Class <?> type, final ShadowClass superclass
) {
return __classCache__.computeIfAbsent (
type, t -> new LambdaShadowClass (
__uniqueId__.getAndIncrement (),
Type.getType (t), null, superclass
)
);
}
//
@Override @Theory
public void getNameMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.getNameMatchesReflection (type);
}
@Override @Theory
public void getSimpleNameMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.getSimpleNameMatchesReflection (type);
}
@Override @Theory
public void getCanonicalNameMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.getCanonicalNameMatchesReflection (type);
}
//
@Override @Theory
public void isPrimitiveMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.isPrimitiveMatchesReflection (type);
}
@Override @Theory
public void isArrayMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.isArrayMatchesReflection (type);
}
@Override @Theory
public void isEnumMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.isEnumMatchesReflection (type);
}
@Override @Theory
public void isInterfaceMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.isInterfaceMatchesReflection (type);
}
@Override @Theory
public void isAnnotationMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.isAnnotationMatchesReflection (type);
}
@Override
@Theory
public void isSyntheticMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.isSyntheticMatchesReflection (type);
}
//
@Override @Theory
public void getModifiersMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.getModifiersMatchesReflection (type);
}
//
@Override @Theory
public void isInstanceOnSelfMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.isInstanceOnSelfMatchesReflection (type);
}
@Override @Theory
public void isAssignableOnSelfMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.isAssignableOnSelfMatchesReflection (type);
}
//
@Override @Theory @Ignore
public void getInterfaceDescriptorsMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
// TODO Enable when we have full information about lambda types.
super.getInterfaceDescriptorsMatchesReflection (type);
}
//
@Override @Theory
public void getDeclaredFieldsMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.getDeclaredFieldsMatchesReflection (type);
}
@Override @Theory
public void getFieldsMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
super.getFieldsMatchesReflection (type);
}
//
@Override @Theory @Ignore
public void getDeclaredMethodsMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
// TODO Enable when we have full information about lambda types.
super.getDeclaredMethodsMatchesReflection (type);
}
@Override @Theory @Ignore
public void getMethodsMatchesReflection (@ParametersSuppliedBy (LambdaTypeSupplier.class) final Class <?> type) {
// TODO Enable when we have full information about lambda types.
super.getMethodsMatchesReflection (type);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment