diff options
author | acolyer <acolyer> | 2006-02-24 12:35:43 +0000 |
---|---|---|
committer | acolyer <acolyer> | 2006-02-24 12:35:43 +0000 |
commit | 5be99e59411e13ed9e779c96811d4d4ca947fe94 (patch) | |
tree | 75f553bbda6ea54ef0f40f8bc9761535505f9fad | |
parent | d667d9eeb975c3de1953e5057209dee451b62231 (diff) | |
download | aspectj-5be99e59411e13ed9e779c96811d4d4ca947fe94.tar.gz aspectj-5be99e59411e13ed9e779c96811d4d4ca947fe94.zip |
Completed: Improve ajc memory usage
the last of the big memory hog in AjState is now removed.
3 files changed, 311 insertions, 139 deletions
diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java index 1ef198bf2..4f6c664c7 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjBuildManager.java @@ -234,6 +234,7 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc } binarySourcesForTheNextCompile = state.getBinaryFilesToCompile(true); performCompilation(buildConfig.getFiles()); + state.clearBinarySourceFiles(); // we don't want these hanging around... if (handler.hasErrors()) { CompilationAndWeavingContext.leavingPhase(ct); return false; @@ -815,8 +816,8 @@ public class AjBuildManager implements IOutputClassFileNameProvider,IBinarySourc //System.out.println("compiling"); environment = getLibraryAccess(classpaths, filenames); - if (!state.getClassNameToUCFMap().isEmpty()) { - environment = new StatefulNameEnvironment(environment, state.getClassNameToUCFMap()); + if (!state.getClassNameToFileMap().isEmpty()) { + environment = new StatefulNameEnvironment(environment, state.getClassNameToFileMap()); } org.aspectj.ajdt.internal.compiler.CompilerAdapter.setCompilerAdapterFactory(this); diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjState.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjState.java index e47215ace..e901f057e 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjState.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/AjState.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -36,11 +37,19 @@ import org.aspectj.bridge.SourceLocation; import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult; import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryField; +import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers; import org.aspectj.org.eclipse.jdt.internal.core.builder.ReferenceCollection; import org.aspectj.org.eclipse.jdt.internal.core.builder.StringSet; +import org.aspectj.org.eclipse.jdt.internal.core.util.Util; import org.aspectj.util.FileUtil; import org.aspectj.weaver.IWeaver; +import org.aspectj.weaver.ReferenceType; +import org.aspectj.weaver.ReferenceTypeDelegate; +import org.aspectj.weaver.ResolvedMember; import org.aspectj.weaver.ResolvedType; +import org.aspectj.weaver.UnresolvedType; import org.aspectj.weaver.bcel.BcelWeaver; import org.aspectj.weaver.bcel.BcelWorld; import org.aspectj.weaver.bcel.UnwovenClassFile; @@ -71,7 +80,8 @@ public class AjState { /** * Keeps a list of (FQN,Filename) pairs (as ClassFile objects) * for types that resulted from the compilation of the given - * File. + * File. Note :- the ClassFile objects contain no byte code, + * they are simply a Filename,typename pair. * * Populated in noteResult and used in addDependentsOf(File) * @@ -86,7 +96,7 @@ public class AjState { * * Added by AMC during state refactoring, 1Q06. */ - private Set sourceFilesDefiningAspects = new HashSet(); + private Set/*<File>*/ sourceFilesDefiningAspects = new HashSet(); /** * Populated in noteResult to record the set of types that should be recompiled if @@ -99,7 +109,9 @@ public class AjState { /** * Holds UnwovenClassFiles (byte[]s) originating from the given file source. This * could be a jar file, a directory, or an individual .class file. This is an - * *expensive* map we would like to release much earlier if possible. + * *expensive* map. It is cleared immediately following a batch build, and the + * cheaper inputClassFilesBySource map is kept for processing of any subsequent + * incremental builds. * * Populated during AjBuildManager.initBcelWorld(). * @@ -116,11 +128,24 @@ public class AjState { * */ private Map/*File, List<UnwovenClassFile>*/ binarySourceFiles = new HashMap(); + + /** + * Initially a duplicate of the information held in binarySourceFiles, with the + * key difference that the values are ClassFiles (type name, File) not UnwovenClassFiles + * (which also have all the byte code in them). After a batch build, binarySourceFiles + * is cleared, leaving just this much lighter weight map to use in processing + * subsequent incremental builds. + */ + private Map/*<File,List<ClassFile>*/ inputClassFilesBySource = new HashMap(); + + /** + * Holds structure information on types as they were at the end of the last + * build. It would be nice to get rid of this too, but can't see an easy way to do + * that right now. + */ + private Map/*FQN,CompactStructureRepresentation*/ resolvedTypeStructuresFromLastBuild = new HashMap(); /** - * The third of the three expensive collections of state held by AjState to support - * incremental compilation. FQN -> UCF. - * * Populated in noteResult to record the set of UnwovenClassFiles (intermediate results) * that originated from compilation of the class with the given fully-qualified name. * @@ -129,7 +154,7 @@ public class AjState { * Passed into StatefulNameEnvironment during incremental compilation to support * findType lookups. */ - private Map/*<String, UnwovenClassFile>*/ classesFromName = new HashMap(); + private Map/*<String, File>*/ classesFromName = new HashMap(); private List/*File*/ compiledSourceFiles = new ArrayList(); @@ -319,7 +344,7 @@ public class AjState { * lastSBT is the last build time for the state asking the question */ private boolean hasStructuralChangedSince(File file,long lastSuccessfulBuildTime) { - long lastModTime = file.lastModified(); + //long lastModTime = file.lastModified(); Long l = (Long)structuralChangesSinceLastFullBuild.get(file.getAbsolutePath()); long strucModTime = -1; if (l!=null) strucModTime = l.longValue(); @@ -426,6 +451,9 @@ public class AjState { ucfs.add(ucf); addDependentsOf(ucf.getClassName()); binarySourceFiles.put(bsf.binSrc.getPath(),ucfs); + List cfs = new ArrayList(1); + cfs.add(getClassFileFor(ucf)); + this.inputClassFilesBySource.put(bsf.binSrc.getPath(), cfs); toWeave.put(bsf.binSrc.getPath(),ucfs); } deleteBinaryClassFiles(); @@ -441,20 +469,16 @@ public class AjState { */ private void removeAllResultsOfLastBuild() { // remove all binarySourceFiles, and all classesFromName... - for (Iterator iter = binarySourceFiles.values().iterator(); iter.hasNext();) { - List ucfs = (List) iter.next(); - for (Iterator iterator = ucfs.iterator(); iterator.hasNext();) { - UnwovenClassFile ucf = (UnwovenClassFile) iterator.next(); - try { - ucf.deleteRealFile(); - } catch (IOException ex) { /* we did our best here */ } + for (Iterator iter = this.inputClassFilesBySource.values().iterator(); iter.hasNext();) { + List cfs = (List) iter.next(); + for (Iterator iterator = cfs.iterator(); iterator.hasNext();) { + ClassFile cf = (ClassFile) iterator.next(); + cf.deleteFromFileSystem(); } } for (Iterator iterator = classesFromName.values().iterator(); iterator.hasNext();) { - UnwovenClassFile ucf = (UnwovenClassFile) iterator.next(); - try { - ucf.deleteRealFile(); - } catch (IOException ex) { /* we did our best here */ } + File f = (File) iterator.next(); + new ClassFile("",f).deleteFromFileSystem(); } for (Iterator iter = resources.iterator(); iter.hasNext();) { String resource = (String) iter.next(); @@ -484,15 +508,11 @@ public class AjState { // range of bsf is ucfs, domain is files (.class and jars) in inpath/jars for (Iterator iter = deletedBinaryFiles.iterator(); iter.hasNext();) { AjBuildConfig.BinarySourceFile deletedFile = (AjBuildConfig.BinarySourceFile) iter.next(); - List ucfs = (List) binarySourceFiles.get(deletedFile.binSrc.getPath()); - binarySourceFiles.remove(deletedFile.binSrc.getPath()); - - // AMC temp during refactoring - UnwovenClassFile ucf = (UnwovenClassFile) ucfs.get(0); - ClassFile cf = new ClassFile(ucf.getClassName(),new File(ucf.getFilename())); - // end temp - - deleteClassFile(cf); + List cfs = (List) this.inputClassFilesBySource.get(deletedFile.binSrc.getPath()); + for (Iterator iterator = cfs.iterator(); iterator.hasNext();) { + deleteClassFile((ClassFile)iterator.next()); + } + this.inputClassFilesBySource.remove(deletedFile.binSrc.getPath()); } } @@ -584,9 +604,9 @@ public class AjState { UnwovenClassFile[] unwovenClassFiles = result.unwovenClassFiles(); for (int i = 0; i < unwovenClassFiles.length; i++) { - UnwovenClassFile lastTimeRound = (UnwovenClassFile) classesFromName.get(unwovenClassFiles[i].getClassName()); + File lastTimeRound = (File) classesFromName.get(unwovenClassFiles[i].getClassName()); recordClassFile(unwovenClassFiles[i],lastTimeRound); - classesFromName.put(unwovenClassFiles[i].getClassName(),unwovenClassFiles[i]); + classesFromName.put(unwovenClassFiles[i].getClassName(),new File(unwovenClassFiles[i].getFilename())); } // need to do this before types are deleted from the World... @@ -713,111 +733,176 @@ public class AjState { return null; } - private void recordClassFile(UnwovenClassFile thisTime, UnwovenClassFile lastTime) { - if (simpleStrings == null) return; // batch build + private void recordClassFile(UnwovenClassFile thisTime, File lastTime) { + if (simpleStrings == null) { + // batch build + // record resolved type for structural comparisions in future increments + // this records a second reference to a structure already held in memory + // by the world. + ResolvedType rType = world.resolve(thisTime.getClassName()); + if (!rType.isMissing()) { + this.resolvedTypeStructuresFromLastBuild.put(thisTime.getClassName(),new CompactStructureRepresentation(rType)); + } + return; + } + CompactStructureRepresentation existingStructure = (CompactStructureRepresentation) this.resolvedTypeStructuresFromLastBuild.get(thisTime.getClassName()); + ReferenceType newResolvedType = (ReferenceType) world.resolve(thisTime.getClassName()); + if (!newResolvedType.isMissing()) { + this.resolvedTypeStructuresFromLastBuild.put(thisTime.getClassName(),new CompactStructureRepresentation(newResolvedType)); + } + if (lastTime == null) { addDependentsOf(thisTime.getClassName()); return; } - byte[] newBytes = thisTime.getBytes(); - byte[] oldBytes = lastTime.getBytes(); - boolean bytesEqual = (newBytes.length == oldBytes.length); - for (int i = 0; (i < oldBytes.length) && bytesEqual; i++) { - if (newBytes[i] != oldBytes[i]) bytesEqual = false; + if (newResolvedType.isMissing()) { + return; } - if (!bytesEqual) { - try { - ClassFileReader reader = new ClassFileReader(oldBytes, lastTime.getFilename().toCharArray()); - // ignore local types since they're only visible inside a single method - if (!(reader.isLocal() || reader.isAnonymous()) && - reader.hasStructuralChanges(newBytes)) { + + byte[] newBytes = thisTime.getBytes(); + try { + ClassFileReader reader = new ClassFileReader(newBytes, lastTime.getAbsolutePath().toCharArray()); + // ignore local types since they're only visible inside a single method + if (!(reader.isLocal() || reader.isAnonymous())) { + if (hasStructuralChanges(reader,existingStructure)) { structuralChangesSinceLastFullBuild.put(thisTime.getFilename(),new Long(currentBuildTime)); - addDependentsOf(lastTime.getClassName()); + addDependentsOf(new String(reader.getName()).replace('/','.')); } - } catch (ClassFormatException e) { - addDependentsOf(lastTime.getClassName()); - } - } + } + } catch (ClassFormatException e) { + addDependentsOf(thisTime.getClassName()); + } } + + /** + * Compare the class structure of the new intermediate (unwoven) class with the + * existingResolvedType of the same class that we have in the world, looking for + * any structural differences (and ignoring aj members resulting from weaving....) + * + * Warning : long but boring method implementation... + * @param reader + * @param existingType + * @return + */ + private boolean hasStructuralChanges(ClassFileReader reader, CompactStructureRepresentation existingType) { + // mirrors the checks in ClassFileReader.hasStructuralChanges, but compares against + // ref type delegate instead of old bytes + if (existingType == null) { + return true; + } + + // modifiers + if (!modifiersEqual(reader.getModifiers(),existingType.modifiers)) { + return true; + } + + // generic signature + if (!equal(reader.getGenericSignature(),existingType.genericSignature)) { + return true; + } + + // superclass name + if (!equal(reader.getSuperclassName(),existingType.superclassName)) { + return true; + } + + // interfaces + char[][] existingIfs = existingType.interfaces; + char[][] newIfsAsChars = reader.getInterfaceNames(); + if (newIfsAsChars == null) { newIfsAsChars = new char[0][]; } + char[][] newIfs = new char[newIfsAsChars.length][]; + if (existingIfs.length != newIfs.length) { + return true; + } + new_interface_loop: for (int i = 0; i < newIfs.length; i++) { + for (int j = 0; j < existingIfs.length; j++) { + if (equal(existingIfs[j],newIfs[i])) { + continue new_interface_loop; + } + } + return true; + } + + // fields + MemberStructure[] existingFields = existingType.fields; + IBinaryField[] newFields = reader.getFields(); + if (newFields == null) { newFields = new IBinaryField[0]; } + if (newFields.length != existingFields.length) { + return true; + } + new_field_loop: for (int i = 0; i < newFields.length; i++) { + char[] fieldName = newFields[i].getName(); + for (int j = 0; j < existingFields.length; j++) { + if (equal(existingFields[j].name,fieldName)) { + if (!modifiersEqual(newFields[i].getModifiers(),existingFields[j].modifiers)) { + return true; + } + if (!equal(existingFields[j].signature,newFields[i].getTypeName())) { + return true; + } + continue new_field_loop; + } + } + return true; + } + + // methods + MemberStructure[] existingMethods = existingType.methods; + IBinaryMethod[] newMethods = reader.getMethods(); + if (newMethods == null) { newMethods = new IBinaryMethod[0]; } + if (newMethods.length != existingMethods.length) { + return true; + } + new_method_loop: for (int i = 0; i < newMethods.length; i++) { + char[] methodName = newMethods[i].getSelector(); + for (int j = 0; j < existingMethods.length; j++) { + if (equal(existingMethods[j].name,methodName)) { + // candidate match + if (!equal(newMethods[i].getMethodDescriptor(),existingMethods[j].signature)) { + continue; // might be overloading + } + else { + // matching sigs + if (!modifiersEqual(newMethods[i].getModifiers(),existingMethods[j].modifiers)) { + return true; + } + continue new_method_loop; + } + } + } + return true; // (no match found) + } + + return false; + } -// public void noteClassesFromFile(CompilationResult result, String sourceFileName, List unwovenClassFiles) { -// File sourceFile = new File(sourceFileName); -// -// if (result != null) { -// references.put(sourceFile, new ReferenceCollection(result.qualifiedReferences, result.simpleNameReferences)); -// } -// -// List previous = (List)classesFromFile.get(sourceFile); -// List newClassFiles = new ArrayList(); -// for (Iterator i = unwovenClassFiles.iterator(); i.hasNext();) { -// UnwovenClassFile cf = (UnwovenClassFile) i.next(); -// cf = writeClassFile(cf, findAndRemoveClassFile(cf.getClassName(), previous)); -// newClassFiles.add(cf); -// classesFromName.put(cf.getClassName(), cf); -// } -// -// if (previous != null && !previous.isEmpty()) { -// for (Iterator i = previous.iterator(); i.hasNext();) { -// UnwovenClassFile cf = (UnwovenClassFile) i.next(); -// deleteClassFile(cf); -// } -// } -// -// classesFromFile.put(sourceFile, newClassFiles); -// resultsFromFile.put(sourceFile, result); -// } -// -// private UnwovenClassFile findAndRemoveClassFile(String name, List previous) { -// if (previous == null) return null; -// for (Iterator i = previous.iterator(); i.hasNext();) { -// UnwovenClassFile cf = (UnwovenClassFile) i.next(); -// if (cf.getClassName().equals(name)) { -// i.remove(); -// return cf; -// } -// } -// return null; -// } -// -// private UnwovenClassFile writeClassFile(UnwovenClassFile cf, UnwovenClassFile previous) { -// if (simpleStrings == null) { // batch build -// addedClassFiles.add(cf); -// return cf; -// } -// -// try { -// if (previous == null) { -// addedClassFiles.add(cf); -// addDependentsOf(cf.getClassName()); -// return cf; -// } -// -// byte[] oldBytes = previous.getBytes(); -// byte[] newBytes = cf.getBytes(); -// //if (this.compileLoop > 1) { // only optimize files which were recompiled during the dependent pass, see 33990 -// notEqual : if (newBytes.length == oldBytes.length) { -// for (int i = newBytes.length; --i >= 0;) { -// if (newBytes[i] != oldBytes[i]) break notEqual; -// } -// //addedClassFiles.add(previous); //!!! performance wasting -// buildManager.bcelWorld.addSourceObjectType(previous.getJavaClass()); -// return previous; // bytes are identical so skip them -// } -// //} -// ClassFileReader reader = new ClassFileReader(oldBytes, previous.getFilename().toCharArray()); -// // ignore local types since they're only visible inside a single method -// if (!(reader.isLocal() || reader.isAnonymous()) && reader.hasStructuralChanges(newBytes)) { -// addDependentsOf(cf.getClassName()); -// } -// } catch (ClassFormatException e) { -// addDependentsOf(cf.getClassName()); -// } -// addedClassFiles.add(cf); -// return cf; -// } + private boolean modifiersEqual(int eclipseModifiers, int resolvedTypeModifiers) { + resolvedTypeModifiers = resolvedTypeModifiers & CompilerModifiers.AccJustFlag; + eclipseModifiers = eclipseModifiers & CompilerModifiers.AccJustFlag; + if ((eclipseModifiers & CompilerModifiers.AccSuper) != 0) { + eclipseModifiers -= CompilerModifiers.AccSuper; + } + return (eclipseModifiers == resolvedTypeModifiers); + } + + private boolean equal(char[] c1, char[] c2) { + if (c1 == null && c2 == null) { + return true; + } + if (c1 == null || c2 == null) { + return false; + } + if (c1.length != c2.length) { + return false; + } + for (int i = 0; i < c1.length; i++) { + if (c1[i] != c2[i]) return false; + } + return true; + } private static StringSet makeStringSet(List strings) { StringSet ret = new StringSet(strings.size()); @@ -943,13 +1028,28 @@ public class AjState { public void recordBinarySource(String fromPathName, List unwovenClassFiles) { this.binarySourceFiles.put(fromPathName,unwovenClassFiles); + List simpleClassFiles = new LinkedList(); + for (Iterator iter = unwovenClassFiles.iterator(); iter.hasNext();) { + UnwovenClassFile ucf = (UnwovenClassFile) iter.next(); + ClassFile cf = getClassFileFor(ucf); + simpleClassFiles.add(cf); + } + this.inputClassFilesBySource.put(fromPathName,simpleClassFiles); + } + + /** + * @param ucf + * @return + */ + private ClassFile getClassFileFor(UnwovenClassFile ucf) { + return new ClassFile(ucf.getClassName(),new File(ucf.getFilename())); } public Map getBinarySourceMap() { return this.binarySourceFiles; } - public Map getClassNameToUCFMap() { + public Map getClassNameToFileMap() { return this.classesFromName; } @@ -1012,4 +1112,59 @@ public class AjState { } } + private static class CompactStructureRepresentation { + + public CompactStructureRepresentation(ResolvedType forType) { + this.className = forType.getName().replace('.','/').toCharArray(); + this.modifiers = forType.getModifiers(); + this.genericSignature = forType.getGenericSignature().toCharArray(); + if (this.genericSignature.length == 0) { + this.genericSignature = null; + } + this.superclassName = forType.getSuperclass().getName().replace('.','/').toCharArray(); + ResolvedType[] rTypes = forType.getDeclaredInterfaces(); + this.interfaces = new char[rTypes.length][]; + for (int i = 0; i < rTypes.length; i++) { + this.interfaces[i] = rTypes[i].getName().replace('.','/').toCharArray(); + } + ResolvedMember[] rFields = forType.getDeclaredFields(); + this.fields = new MemberStructure[rFields.length]; + for (int i = 0; i < rFields.length; i++) { + this.fields[i] = new MemberStructure(); + this.fields[i].name = rFields[i].getName().toCharArray(); + this.fields[i].modifiers = rFields[i].getModifiers(); + this.fields[i].signature = rFields[i].getReturnType().getSignature().toCharArray(); + } + ResolvedMember[] rMethods = forType.getDeclaredMethods(); + this.methods = new MemberStructure[rMethods.length]; + for (int i = 0; i < rMethods.length; i++) { + this.methods[i] = new MemberStructure(); + this.methods[i].name = rMethods[i].getName().toCharArray(); + this.methods[i].modifiers = rMethods[i].getModifiers(); + StringBuffer sig = new StringBuffer(); + sig.append("("); + UnresolvedType[] pTypes = rMethods[i].getParameterTypes(); + for (int j = 0; j < pTypes.length; j++) { + sig.append(pTypes[j].getSignature()); + } + sig.append(")"); + sig.append(rMethods[i].getReturnType().getSignature()); + this.methods[i].signature = sig.toString().toCharArray(); + } + } + + char[] className; + int modifiers; + char[] genericSignature; + char[] superclassName; + char[][] interfaces; + MemberStructure[] fields; + MemberStructure[] methods; + } + + private static class MemberStructure { + char[] name; + int modifiers; + char[] signature; + } } diff --git a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/StatefulNameEnvironment.java b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/StatefulNameEnvironment.java index 8d00916d8..11524e9e8 100644 --- a/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/StatefulNameEnvironment.java +++ b/org.aspectj.ajdt.core/src/org/aspectj/ajdt/internal/core/builder/StatefulNameEnvironment.java @@ -14,27 +14,32 @@ package org.aspectj.ajdt.internal.core.builder; //import java.util.HashMap; +import java.io.File; +import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; -import org.aspectj.weaver.bcel.UnwovenClassFile; import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation; import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.aspectj.org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.aspectj.org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.aspectj.util.FileUtil; public class StatefulNameEnvironment implements INameEnvironment { private Map classesFromName; + private Map inflatedClassFilesCache; private Set packageNames; private INameEnvironment baseEnvironment; public StatefulNameEnvironment(INameEnvironment baseEnvironment, Map classesFromName) { this.classesFromName = classesFromName; + this.inflatedClassFilesCache = new HashMap(); this.baseEnvironment = baseEnvironment; packageNames = new HashSet(); @@ -60,18 +65,29 @@ public class StatefulNameEnvironment implements INameEnvironment { } private NameEnvironmentAnswer findType(String name) { - UnwovenClassFile cf = (UnwovenClassFile)classesFromName.get(name); - //System.err.println("find: " + name + " found: " + cf); - - if (cf == null) return null; - - try { - //System.out.println("from cache: " + name); - return new NameEnvironmentAnswer( - new ClassFileReader(cf.getBytes(), cf.getFilename().toCharArray()), - null /* no access restriction */); - } catch (ClassFormatException e) { - return null; //!!! seems to match FileSystem behavior + if (this.inflatedClassFilesCache.containsKey(name)) { + return (NameEnvironmentAnswer) this.inflatedClassFilesCache.get(name); + } + else { + File fileOnDisk = (File)classesFromName.get(name); + //System.err.println("find: " + name + " found: " + cf); + + if (fileOnDisk == null) return null; + + try { + //System.out.println("from cache: " + name); + byte[] bytes = FileUtil.readAsByteArray(fileOnDisk); + NameEnvironmentAnswer ret = + new NameEnvironmentAnswer( + new ClassFileReader(bytes, fileOnDisk.getAbsolutePath().toCharArray()), + null /* no access restriction */); + this.inflatedClassFilesCache.put(name,ret); + return ret; + } catch (ClassFormatException e) { + return null; //!!! seems to match FileSystem behavior + } catch (IOException ex) { + return null; // see above... + } } } |