Commit 083b1191 authored by Lubomir Bulej's avatar Lubomir Bulej

- Simplify implementation of classes derived from ShadowClass by moving as

  much common code to ShadowClass. Also avoid duplication information that
  can be easily extracted from ASM Type or ClassNode.
- Add test that test the basic functionality of the three concrete
  subclasses of ShadowClass.
- Modify build.xml to pick up the new tests.
parent d0a6a0e0
......@@ -803,6 +803,7 @@
<include name="**/junit/*Test.java"/>
<include name="**/disl/*Test.java"/>
<include name="**/disl/scope/*Test.java"/>
<include name="**/dislreserver/shadow/*Test.java"/>
</fileset>
</ac:else>
</ac:if>
......
......@@ -2,7 +2,7 @@ package ch.usi.dag.dislreserver.shadow;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.stream.Stream;
import org.objectweb.asm.Type;
......@@ -26,7 +26,7 @@ final class ArrayShadowClass extends ShadowClass {
__componentClass = componentClass;
}
//
//
public int getDimensionCount () {
return _type ().getDimensions ();
......@@ -80,13 +80,17 @@ final class ArrayShadowClass extends ShadowClass {
return false;
}
//
//
@Override
public int getModifiers () {
// Array classes are ABSTRACT and FINAL, privacy depends on the
// privacy of the component type. Until we get valid component type,
// we will make the array classes public.
//
// Array classes are ABSTRACT and FINAL, but the access modifier is
// derived from the component type. We make the array classes public
// until we have a valid component type.
//
// FIXME Return access modifier based on the component type.
//
return Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC;
}
......@@ -115,70 +119,16 @@ final class ArrayShadowClass extends ShadowClass {
//
@Override
public FieldInfo getField (final String fieldName) throws NoSuchFieldException {
throw new NoSuchFieldException (getCanonicalName () + "." + fieldName);
}
@Override
public FieldInfo [] getFields () {
return new FieldInfo [0];
}
//
@Override
public FieldInfo getDeclaredField (final String fieldName)
throws NoSuchFieldException {
throw new NoSuchFieldException (getCanonicalName () + "." + fieldName);
}
@Override
public FieldInfo [] getDeclaredFields () {
return new FieldInfo [0];
}
//
@Override
public MethodInfo getMethod (final String methodName, final String [] argumentNames)
throws NoSuchMethodException {
for (final MethodInfo methodInfo : __superClass.getMethods ()) {
if (methodName.equals (methodInfo.getName ())
&& Arrays.equals (argumentNames, methodInfo.getParameterDescriptors ())
) {
return methodInfo;
}
}
throw new NoSuchMethodException (
getCanonicalName () + "." + methodName + _descriptorsToString (argumentNames)
);
}
@Override
public MethodInfo [] getMethods () {
return getSuperclass ().getMethods ();
}
//
@Override
public MethodInfo getDeclaredMethod (final String methodName,
final String [] argumentNames)
throws NoSuchMethodException {
throw new NoSuchMethodException (
getCanonicalName () + "." + methodName + _descriptorsToString (argumentNames)
);
protected Stream <FieldInfo> _declaredFields () {
// Array types have no declared fields.
return Stream.empty ();
}
@Override
public MethodInfo [] getDeclaredMethods () {
return new MethodInfo [0];
protected Stream <MethodInfo> _declaredMethods () {
// Array types have no declared methods.
return Stream.empty ();
}
}
......@@ -20,6 +20,39 @@ public final class MethodInfo {
//
@Override
public int hashCode () {
return
(this.getName ().hashCode () & 0xFFFF0000)
|
(this.getDescriptor ().hashCode () & 0x0000FFFF);
}
@Override
public boolean equals (final Object object) {
if (object instanceof MethodInfo) {
final MethodInfo other = (MethodInfo) object;
return
this.getName ().equals (other.getName ())
&&
this.getDescriptor ().equals (other.getDescriptor ());
}
return false;
}
//
boolean matches (final String name, final String [] paramDescriptors) {
return
this.getName ().equals (name)
&&
Arrays.equals (paramDescriptors, this.getParameterDescriptors ());
}
//
public String getName () {
return __methodNode.name;
}
......@@ -58,4 +91,14 @@ public final class MethodInfo {
return Modifier.isPublic (getModifiers ());
}
public boolean isConstructor () {
return "<init>".equals (__methodNode.name);
}
public boolean isClassInitializer () {
return "<clinit>".equals (__methodNode.name);
}
}
package ch.usi.dag.dislreserver.shadow;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.lang.reflect.Modifier;
import java.util.stream.Stream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.MethodNode;
import ch.usi.dag.dislreserver.DiSLREServerFatalException;
final class ObjectShadowClass extends ShadowClass {
// TODO ! is this implementation of methods really working ??
private final ShadowClass __superClass;
private ClassNode __classNode;
private String __name;
private final ClassNode __classNode;
//
ObjectShadowClass (
final long netReference, final Type type,
final ShadowObject classLoader, final ShadowClass superClass,
final byte [] classCode
final ClassNode classNode
) {
super (netReference, type, classLoader);
__superClass = superClass;
if (classCode == null || classCode.length == 0) {
throw new DiSLREServerFatalException (
"Creating class info for "+ type + " with no code provided"
);
}
initializeClassInfo (classCode);
}
private List <MethodInfo> methods;
private List <MethodInfo> public_methods;
private List <FieldInfo> fields;
private List <FieldInfo> public_fields;
private List <String> innerclasses;
private void initializeClassInfo (final byte [] classCode) {
final ClassReader classReader = new ClassReader (classCode);
__classNode = new ClassNode (Opcodes.ASM4);
classReader.accept (__classNode, ClassReader.SKIP_DEBUG | ClassReader.EXPAND_FRAMES);
__name = __classNode.name.replace ('/', '.');
methods = new ArrayList <MethodInfo> (__classNode.methods.size ());
public_methods = new LinkedList <MethodInfo> ();
for (final MethodNode methodNode : __classNode.methods) {
final MethodInfo methodInfo = new MethodInfo (methodNode);
methods.add (methodInfo);
if (methodInfo.isPublic ()) {
public_methods.add (methodInfo);
}
}
fields = new ArrayList <FieldInfo> (__classNode.fields.size ());
public_fields = new LinkedList <FieldInfo> ();
for (final FieldNode fieldNode : __classNode.fields) {
final FieldInfo fieldInfo = new FieldInfo (fieldNode);
fields.add (fieldInfo);
if (fieldInfo.isPublic ()) {
public_fields.add (fieldInfo);
}
}
if (getSuperclass () != null) {
for (final MethodInfo methodInfo : getSuperclass ().getMethods ()) {
public_methods.add (methodInfo);
}
for (final FieldInfo fieldInfo : getSuperclass ().getFields ()) {
public_fields.add (fieldInfo);
}
}
innerclasses = new ArrayList <String> (__classNode.innerClasses.size ());
for (final InnerClassNode innerClassNode : __classNode.innerClasses) {
innerclasses.add (innerClassNode.name);
}
__classNode = classNode;
}
//
//
/**
* @see Class#isInstance(Object)
......@@ -132,14 +55,15 @@ final class ObjectShadowClass extends ShadowClass {
throw new UnsupportedOperationException ("not yet implemented");
}
//
//
@Override
public int getModifiers () {
return __classNode.access;
// Strip modifiers that are not valid for a class.
return __classNode.access & Modifier.classModifiers ();
}
//
//
/**
* @see Class#getSuperclass()
......@@ -158,109 +82,35 @@ final class ObjectShadowClass extends ShadowClass {
@Override
public String [] getInterfaceDescriptors () {
final List <String> interfaces = __classNode.interfaces;
return interfaces.toArray (new String [interfaces.size ()]);
}
//
@Override
public FieldInfo [] getFields () {
// to have "checked" array :(
return public_fields.toArray (new FieldInfo [0]);
return __typeDescriptors (__classNode.interfaces.stream ());
}
//
@Override
public FieldInfo getField (final String fieldName) throws NoSuchFieldException {
for (final FieldInfo fieldInfo : fields) {
if (fieldInfo.isPublic () && fieldInfo.getName ().equals (fieldName)) {
return fieldInfo;
}
}
if (getSuperclass () == null) {
throw new NoSuchFieldException (__name + "." + fieldName);
}
return getSuperclass ().getField (fieldName);
protected Stream <FieldInfo> _declaredFields () {
return __classNode.fields.stream ().map (FieldInfo::new);
}
@Override
public MethodInfo [] getMethods () {
// to have "checked" array :(
return public_methods.toArray (new MethodInfo [0]);
protected Stream <MethodInfo> _declaredMethods () {
return __classNode.methods.stream ().map (MethodInfo::new);
}
//
@Override
public MethodInfo getMethod (
final String methodName, final String [] argumentNames
) throws NoSuchMethodException {
for (final MethodInfo methodInfo : public_methods) {
if (methodName.equals (methodInfo.getName ()) &&
Arrays.equals (argumentNames, methodInfo.getParameterDescriptors ())
) {
return methodInfo;
}
}
throw new NoSuchMethodException (
__name + "." + methodName + _descriptorsToString (argumentNames)
public String [] getDeclaredClassDescriptors () {
return __typeDescriptors (
__classNode.innerClasses.stream ().map (icn -> icn.name)
);
}
//
@Override
public FieldInfo [] getDeclaredFields () {
return fields.toArray (new FieldInfo [0]);
}
@Override
public FieldInfo getDeclaredField (final String fieldName)
throws NoSuchFieldException {
for (final FieldInfo fieldInfo : fields) {
if (fieldInfo.getName ().equals (fieldName)) {
return fieldInfo;
}
}
throw new NoSuchFieldException (__name + "." + fieldName);
}
@Override
public MethodInfo [] getDeclaredMethods () {
return methods.toArray (new MethodInfo [methods.size ()]);
}
@Override
public MethodInfo getDeclaredMethod (
final String methodName, final String [] argumentNames
) throws NoSuchMethodException {
for (final MethodInfo methodInfo : methods) {
if (methodName.equals (methodInfo.getName ()) &&
Arrays.equals (argumentNames, methodInfo.getParameterDescriptors ())
) {
return methodInfo;
}
}
throw new NoSuchMethodException (
__name + "." + methodName + _descriptorsToString (argumentNames)
);
private static String [] __typeDescriptors (final Stream <String> names) {
return names.map (n -> Type.getObjectType (n).getDescriptor ()).toArray (String []::new);
}
//
@Override
public String [] getDeclaredClassDescriptors () {
return __classNode.innerClasses.stream ().unordered ()
.map (icn -> Type.getObjectType (icn.name).getDescriptor ())
.toArray (String []::new);
}
}
package ch.usi.dag.dislreserver.shadow;
import java.lang.reflect.Modifier;
import java.util.stream.Stream;
import org.objectweb.asm.Type;
......@@ -18,7 +19,9 @@ final class PrimitiveShadowClass extends ShadowClass {
@Override
public int getModifiers () {
//
// Primitive type classes are ABSTRACT, FINAL, and PUBLIC.
//
return Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC;
}
......@@ -52,7 +55,7 @@ final class PrimitiveShadowClass extends ShadowClass {
return getCanonicalName ();
}
//
//
/**
* @see Class#getSuperclass()
......@@ -77,61 +80,16 @@ final class PrimitiveShadowClass extends ShadowClass {
//
@Override
public FieldInfo getField (final String fieldName) throws NoSuchFieldException {
throw new NoSuchFieldException (getCanonicalName () + "." + fieldName);
}
@Override
public FieldInfo [] getFields () {
return new FieldInfo [0];
}
//
@Override
public FieldInfo getDeclaredField (final String fieldName) throws NoSuchFieldException {
throw new NoSuchFieldException (getCanonicalName () + "." + fieldName);
}
@Override
public FieldInfo [] getDeclaredFields () {
return new FieldInfo [0];
}
//
@Override
public MethodInfo [] getMethods () {
return new MethodInfo [0];
}
@Override
public MethodInfo getMethod (
final String methodName, final String [] argumentNames
) throws NoSuchMethodException {
throw new NoSuchMethodException (
getCanonicalName () + "." + methodName + _descriptorsToString (argumentNames)
);
}
//
@Override
public MethodInfo [] getDeclaredMethods () {
return new MethodInfo [0];
protected Stream <FieldInfo> _declaredFields () {
// Primitive types have no declared fields.
return Stream.empty ();
}
@Override
public MethodInfo getDeclaredMethod (
final String methodName, final String [] argumentNames
) throws NoSuchMethodException {
throw new NoSuchMethodException (
getCanonicalName () + "." + methodName + _descriptorsToString (argumentNames)
);
protected Stream <MethodInfo> _declaredMethods () {
// Primitive types have no declared methods.
return Stream.empty ();
}
}
......@@ -7,6 +7,7 @@ import org.objectweb.asm.Type;
import ch.usi.dag.dislreserver.DiSLREServerFatalException;
import ch.usi.dag.dislreserver.util.Logging;
import ch.usi.dag.util.asm.ClassNodeHelper;
import ch.usi.dag.util.logging.Logger;
......@@ -154,14 +155,15 @@ public final class ShadowClassTable {
}
final byte [] classCode = classNameMap.get (type.getClassName ());
if (classCode == null) {
if (classCode == null || classCode.length == 0) {
throw new DiSLREServerFatalException (
"Class "+ type.getClassName () + " has not been loaded"
"No bytecode provided for class "+ type.getClassName ()
);
}
return new ObjectShadowClass (
netReference, type, classLoader, superClass, classCode
netReference, type, classLoader, superClass,
ClassNodeHelper.OUTLINE.unmarshal (classCode)
);
} else {
......
package ch.usi.dag.dislreserver.shadow;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Assert;
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 ArrayShadowClassTest extends ShadowClassTestBase {
private static final AtomicLong __uniqueId__ = new AtomicLong (1);
private static final ShadowClass __objectShadowClass__ = new ObjectShadowClass (
__uniqueId__.getAndIncrement (), Type.getType (Object.class),
null /* no class loader */, null /* no super class */,
createClassNode (Object.class)
);
//
public static class ArrayTypeSupplier extends ParameterSupplier {
@Override
public List <PotentialAssignment> getValueSources (final ParameterSignature sig) {
return _createClassAssignments (new Class <?> [] {
// primitive array types
boolean [].class, byte [].class, char [].class, short [].class,
int [].class, long [].class, float [].class, double [].class,
// reference array types
boolean [][].class, Boolean [].class
});
}
}
//
@Override
protected ShadowClass _createShadowClass (final Class <?> type) {
Assert.assertTrue (type.isArray ());
return new ArrayShadowClass (
__uniqueId__.getAndIncrement (), Type.getType (type),
null /* no class loader */, __objectShadowClass__,
null /* no component type yet */
);
}
//
@Override @Theory
public void getNameMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.getNameMatchesReflection (type);
}
@Override @Theory
public void getSimpleNameMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.getSimpleNameMatchesReflection (type);
}
@Override @Theory
public void getCanonicalNameMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.getCanonicalNameMatchesReflection (type);
}
//
@Override @Theory
public void isPrimitiveMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.isPrimitiveMatchesReflection (type);
}
@Override @Theory
public void isArrayMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.isArrayMatchesReflection (type);
}
@Override @Theory
public void isEnumMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.isEnumMatchesReflection (type);
}
@Override @Theory
public void isInterfaceMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.isInterfaceMatchesReflection (type);
}
@Override @Theory
public void isAnnotationMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.isAnnotationMatchesReflection (type);
}
@Override
@Theory
public void isSyntheticMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.isSyntheticMatchesReflection (type);
}
//
@Override @Theory
public void getModifiersMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.getModifiersMatchesReflection (type);
}
//
@Override @Theory
public void isInstanceOnSelfMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.isInstanceOnSelfMatchesReflection (type);
}
@Override @Theory
public void isAssignableOnSelfMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.isAssignableOnSelfMatchesReflection (type);
}
//
@Override @Theory
public void getInterfaceDescriptorsMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
super.getInterfaceDescriptorsMatchesReflection (type);
}
//
@Override @Theory
public void getDeclaredFieldsMatchesReflection (@ParametersSuppliedBy (ArrayTypeSupplier.class) final Class <?> type) {
// Array types have no declared fields.