Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 78 additions & 7 deletions asm/src/main/java/org/aspectj/asm/AsmManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
Expand All @@ -38,7 +41,10 @@
import org.aspectj.asm.internal.AspectJElementHierarchy;
import org.aspectj.asm.internal.HandleProviderDelimiter;
import org.aspectj.asm.internal.JDTLikeHandleProvider;
import org.aspectj.asm.internal.ProgramElement;
import org.aspectj.asm.internal.Relationship;
import org.aspectj.asm.internal.RelationshipMap;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.util.IStructureModel;

Expand Down Expand Up @@ -248,13 +254,13 @@ public void readStructureModel(String configFilePath) {
hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
} else {
String filePath = genExternFilePath(configFilePath);
FileInputStream in = new FileInputStream(filePath);
ObjectInputStream s = new ObjectInputStream(in);
hierarchy = (AspectJElementHierarchy) s.readObject();
((AspectJElementHierarchy) hierarchy).setAsmManager(this);
hierarchyReadOK = true;
mapper = (RelationshipMap) s.readObject();
s.close();
try (FileInputStream in = new FileInputStream(filePath);
ObjectInputStream s = new StructureModelObjectInputStream(in)) {
hierarchy = (AspectJElementHierarchy) s.readObject();
((AspectJElementHierarchy) hierarchy).setAsmManager(this);
hierarchyReadOK = true;
mapper = (RelationshipMap) s.readObject();
}
}
} catch (FileNotFoundException fnfe) {
// That is OK
Expand Down Expand Up @@ -284,6 +290,71 @@ private String genExternFilePath(String configFilePath) {
return configFilePath + ".ajsym";
}

private static class StructureModelObjectInputStream extends ObjectInputStream {

private static final Set<String> ALLOWED_SERIALIZED_TYPES = new HashSet<>();

static {
ALLOWED_SERIALIZED_TYPES.add(AspectJElementHierarchy.class.getName());
ALLOWED_SERIALIZED_TYPES.add(ProgramElement.class.getName());
ALLOWED_SERIALIZED_TYPES.add(RelationshipMap.class.getName());
ALLOWED_SERIALIZED_TYPES.add(Relationship.class.getName());
ALLOWED_SERIALIZED_TYPES.add(IProgramElement.Accessibility.class.getName());
ALLOWED_SERIALIZED_TYPES.add(IProgramElement.ExtraInformation.class.getName());
ALLOWED_SERIALIZED_TYPES.add(IProgramElement.Kind.class.getName());
ALLOWED_SERIALIZED_TYPES.add(IProgramElement.Modifiers.class.getName());
ALLOWED_SERIALIZED_TYPES.add(IRelationship.Kind.class.getName());
ALLOWED_SERIALIZED_TYPES.add(SourceLocation.class.getName());
ALLOWED_SERIALIZED_TYPES.add(File.class.getName());
ALLOWED_SERIALIZED_TYPES.add(String.class.getName());
ALLOWED_SERIALIZED_TYPES.add(Boolean.class.getName());
ALLOWED_SERIALIZED_TYPES.add(Integer.class.getName());
ALLOWED_SERIALIZED_TYPES.add(ArrayList.class.getName());
ALLOWED_SERIALIZED_TYPES.add(HashMap.class.getName());
ALLOWED_SERIALIZED_TYPES.add(LinkedHashMap.class.getName());
ALLOWED_SERIALIZED_TYPES.add("java.util.Arrays$ArrayList");
ALLOWED_SERIALIZED_TYPES.add("java.util.Collections$EmptyList");
ALLOWED_SERIALIZED_TYPES.add("java.util.Collections$EmptyMap");
ALLOWED_SERIALIZED_TYPES.add("java.util.Collections$SingletonList");
ALLOWED_SERIALIZED_TYPES.add("java.util.Collections$UnmodifiableCollection");
ALLOWED_SERIALIZED_TYPES.add("java.util.Collections$UnmodifiableList");
ALLOWED_SERIALIZED_TYPES.add("java.util.Collections$UnmodifiableRandomAccessList");
}

StructureModelObjectInputStream(FileInputStream in) throws IOException {
super(in);
}

@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String className = desc.getName();
if (!isAllowedSerializedType(className)) {
throw new InvalidClassException(className, "not allowed in AspectJ structure model");
}
return super.resolveClass(desc);
}

private static boolean isAllowedSerializedType(String className) {
if (className.startsWith("[")) {
return isAllowedArrayType(className);
}
return ALLOWED_SERIALIZED_TYPES.contains(className);
}

private static boolean isAllowedArrayType(String className) {
while (className.startsWith("[")) {
className = className.substring(1);
}
if (className.length() == 1) {
return true;
}
if (className.startsWith("L") && className.endsWith(";")) {
return isAllowedSerializedType(className.substring(1, className.length() - 1));
}
return false;
}
}

public String getCanonicalFilePath(File f) {
return canonicalFilePathMap.get(f);
}
Expand Down
168 changes: 168 additions & 0 deletions asm/src/test/java/org/aspectj/asm/AsmManagerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package org.aspectj.asm;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.aspectj.asm.internal.ProgramElement;
import org.aspectj.bridge.SourceLocation;

import junit.framework.TestCase;

public class AsmManagerTest extends TestCase {

public void testReadStructureModelRoundTripRestoresHierarchyAndRelationships() throws Exception {
File configFile = File.createTempFile("asm-manager-test", ".lst");
File sourceFile = File.createTempFile("asm-manager-source", ".java");
File structureModelFile = new File(configFile.getParentFile(), configFile.getName().replace(".lst", ".ajsym"));
try {
AsmManager asm = buildModel(sourceFile);
ProgramElement fileNode = (ProgramElement) asm.getHierarchy().getRoot().getChildren().get(0);
asm.getRelationshipMap().get(fileNode, IRelationship.Kind.ADVICE, "advises", false, true).addTarget("=project/target");
asm.writeStructureModel(configFile.getAbsolutePath());

AsmManager restored = AsmManager.createNewStructureModel(Collections.<File, String>emptyMap());
restored.readStructureModel(configFile.getAbsolutePath());

assertNotSame(IHierarchy.NO_STRUCTURE, restored.getHierarchy().getRoot());
assertEquals("project", restored.getHierarchy().getRoot().getName());
assertNotNull(restored.getHierarchy().findInFileMap(sourceFile.getCanonicalPath()));
assertEquals(1, restored.getRelationshipMap().get(fileNode.getHandleIdentifier()).size());
} finally {
configFile.delete();
sourceFile.delete();
structureModelFile.delete();
}
}

public void testPrePatchRawObjectInputStreamWouldExecuteReadObjectCallback() throws Exception {
File structureModelFile = File.createTempFile("asm-manager-test", ".ajsym");
try {
writeStructureModelPayload(structureModelFile, new UnexpectedSerializedType());
UnexpectedSerializedType.readObjectInvoked = false;
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(structureModelFile))) {
in.readObject();
}
assertTrue(UnexpectedSerializedType.readObjectInvoked);
} finally {
structureModelFile.delete();
}
}

public void testPatchedReadStructureModelRejectsUnexpectedSerializedTypesBeforeReadObject() throws Exception {
File configFile = File.createTempFile("asm-manager-test", ".lst");
File structureModelFile = new File(configFile.getParentFile(), configFile.getName().replace(".lst", ".ajsym"));
try {
writeStructureModelPayload(structureModelFile, new UnexpectedSerializedType());
UnexpectedSerializedType.readObjectInvoked = false;

AsmManager asm = AsmManager.createNewStructureModel(Collections.<File, String>emptyMap());
asm.readStructureModel(configFile.getAbsolutePath());

assertFalse(UnexpectedSerializedType.readObjectInvoked);
assertSame(IHierarchy.NO_STRUCTURE, asm.getHierarchy().getRoot());
} finally {
configFile.delete();
structureModelFile.delete();
}
}

public void testReadStructureModelSupportsArraysAsList() throws Exception {
assertModelRoundTripWithCustomData(new ProgramElementMutator() {
public void mutate(ProgramElement node) {
node.setParameterNames(Arrays.asList("a", "b"));
}
});
}

public void testReadStructureModelSupportsUnmodifiableList() throws Exception {
assertModelRoundTripWithCustomData(new ProgramElementMutator() {
public void mutate(ProgramElement node) {
List<String> values = new ArrayList<>();
values.add("a");
values.add("b");
node.setParameterNames(Collections.unmodifiableList(values));
}
});
}

public void testReadStructureModelSupportsLinkedHashMap() throws Exception {
assertModelRoundTripWithCustomData(new ProgramElementMutator() {
public void mutate(ProgramElement node) {
Map<String, List<String>> map = new LinkedHashMap<>();
map.put("Type", Arrays.asList("Parent"));
node.setDeclareParentsMap(map);
}
});
}

private void assertModelRoundTripWithCustomData(ProgramElementMutator mutator) throws Exception {
File configFile = File.createTempFile("asm-manager-test", ".lst");
File sourceFile = File.createTempFile("asm-manager-source", ".java");
File structureModelFile = new File(configFile.getParentFile(), configFile.getName().replace(".lst", ".ajsym"));
try {
AsmManager asm = buildModel(sourceFile);
ProgramElement fileNode = (ProgramElement) asm.getHierarchy().getRoot().getChildren().get(0);
mutator.mutate(fileNode);
asm.writeStructureModel(configFile.getAbsolutePath());

AsmManager restored = AsmManager.createNewStructureModel(Collections.<File, String>emptyMap());
restored.readStructureModel(configFile.getAbsolutePath());

assertNotSame(IHierarchy.NO_STRUCTURE, restored.getHierarchy().getRoot());
assertNotNull(restored.getHierarchy().findInFileMap(sourceFile.getCanonicalPath()));
} finally {
configFile.delete();
sourceFile.delete();
structureModelFile.delete();
}
}

private AsmManager buildModel(File sourceFile) throws Exception {
AsmManager asm = AsmManager.createNewStructureModel(Collections.<File, String>emptyMap());
ProgramElement root = new ProgramElement(asm, "project", IProgramElement.Kind.PROJECT, null);
ProgramElement fileNode = new ProgramElement(asm, sourceFile.getName(), IProgramElement.Kind.FILE_JAVA,
new SourceLocation(sourceFile, 1, 1, 1), 0, null, null);
fileNode.setHandleIdentifier("=project/" + sourceFile.getName());
root.addChild(fileNode);
asm.getHierarchy().setRoot(root);
HashMap<String, IProgramElement> fileMap = new HashMap<>();
fileMap.put(sourceFile.getCanonicalPath(), fileNode);
asm.getHierarchy().setFileMap(fileMap);
return asm;
}

private static void writeStructureModelPayload(File file, Object payload) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
try {
out.writeObject(payload);
} finally {
out.close();
}
}

private static class UnexpectedSerializedType implements Serializable {
private static final long serialVersionUID = 1L;
private static boolean readObjectInvoked;

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
readObjectInvoked = true;
in.defaultReadObject();
}
}

private interface ProgramElementMutator {
void mutate(ProgramElement node);
}
}
Loading