001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo.tools.enhancer;
012:
013: import com.versant.core.jdo.tools.enhancer.utils.*;
014:
015: import java.util.*;
016: import java.io.*;
017: import java.net.URL;
018:
019: import com.versant.lib.bcel.classfile.*;
020: import com.versant.lib.bcel.generic.*;
021: import com.versant.lib.bcel.Constants;
022: import com.versant.lib.bcel.*;
023:
024: /**
025: * The FieldRefEnhancer catches all the Persistent aware classes
026: */
027: public class FieldRefEnhancer {
028: private ClassLoader loader;
029: private HashMap fieldToEnhanceMap;
030: private ClassGen classGen;
031: private ConstantPoolGen constantPoolGen;
032: private InstructionFactory instructionFactory;
033: private File outputDir;
034: private ArrayList pcList = new ArrayList();
035: private boolean hasChanges;
036: private static final String GET_FIELD = com.versant.lib.bcel.generic.GETFIELD.class
037: .getName();
038: private static final String PUT_FIELD = com.versant.lib.bcel.generic.PUTFIELD.class
039: .getName();
040: private static final String A_LOAD = com.versant.lib.bcel.generic.ALOAD.class
041: .getName();
042: private static final String D_U_P = com.versant.lib.bcel.generic.DUP.class
043: .getName();
044: private int aware;
045: private String fileSeparator;
046: private char charfileSeparator;
047: private File currentOutputFile;
048:
049: public FieldRefEnhancer(File outputDir, ClassLoader loader) {
050: this .outputDir = outputDir;
051: this .loader = loader;
052: fileSeparator = System.getProperty("file.separator");
053: charfileSeparator = fileSeparator.charAt(0);
054: }
055:
056: public void setFieldsToEnhanceMap(HashMap map) {
057: fieldToEnhanceMap = map;
058: }
059:
060: public int getAwareNum() {
061: return aware;
062: }
063:
064: public void setPersistentCapable(String pcClassName) {
065: pcList.add(pcClassName);
066: }
067:
068: public void enhance(Set scopeFiles) {
069: if (fieldToEnhanceMap.isEmpty())
070: return;
071: Iterator iter = scopeFiles.iterator();
072: while (iter.hasNext()) {
073: String fileName = (String) iter.next();
074: try {
075: hasChanges = false;
076: JavaClass javaClass = getJavaClass(fileName);
077: classGen = new ClassGen(javaClass);
078: if (!pcList.contains(classGen.getClassName())) {
079: // ConstantPoolGen is used to represent the constant pool of a Classfile
080: constantPoolGen = classGen.getConstantPool();
081: // used to create objects representing VM instructions
082: instructionFactory = new InstructionFactory(
083: constantPoolGen);
084: swapGetterAndSetter();
085: if (hasChanges) {
086: dumpClass();
087: aware++;
088: System.out.println("Persistent Aware = "
089: + classGen.getClassName());
090: }
091: }
092: } catch (Exception e) {
093: e.printStackTrace(); // TODO fix this
094: }
095: }
096: }
097:
098: private JavaClass getJavaClass(String className) throws IOException {
099: InputStream inputStream = loader.getResourceAsStream(className);
100: if (inputStream == null) {
101: if (inputStream == null) {
102: throw new javax.jdo.JDOFatalUserException(
103: "Class not found: " + className);
104: }
105: }
106: ClassParser parser = new ClassParser(inputStream, className);
107: return parser.parse();
108: }
109:
110: private void dumpClass() throws VerifyException {
111: String fileName = classGen.getClassName().replace('.',
112: charfileSeparator)
113: + ".class";
114: File dumpFile;
115: if (outputDir != null) {
116: dumpFile = new File(outputDir, fileName);
117: } else {
118: URL currentFileURL = loader.getResource(fileName);
119: if (currentFileURL.toString().startsWith("jar:")) {
120: throw new javax.jdo.JDOFatalUserException(
121: "Can not write file "
122: + fileName
123: + " into a jar. Please specify a output directory.");
124: }
125: currentOutputFile = new File(currentFileURL.getFile());
126: dumpFile = currentOutputFile;
127: }
128: try {
129: classGen.getJavaClass().dump(dumpFile);
130: } catch (IOException e) {
131: throw new VerifyException(e);
132: }
133:
134: }
135:
136: private void swapGetterAndSetter() {
137:
138: // representation of methods in the class
139: Method[] methods = classGen.getMethods();
140: for (int i = 0; i < methods.length; i++) {
141: Method m = methods[i];
142:
143: // native and abstract methods don't have any code
144: // so continue with next method
145: if (m.isNative() || m.isAbstract()) {
146: continue;
147: }
148: //we do not want to enhance our enhanced methods
149: if (m.getName().startsWith("jdo")
150: || m.getName().startsWith("<c")) {
151: continue;
152: }
153:
154: boolean changed = false;
155:
156: MethodGen mg = new MethodGen(m, classGen.getClassName(),
157: constantPoolGen);
158:
159: // get the code in form of an InstructionList object
160: InstructionList il = mg.getInstructionList();
161:
162: // get the first instruction
163: InstructionHandle ih = il.getStart();
164: while (ih != null) {
165: Instruction ins = ih.getInstruction();
166: if (ins.getClass().getName().equals(GET_FIELD)) {//if (ins instanceof GETFIELD)
167: GETFIELD is = (GETFIELD) ins;
168: if (!is.getFieldName(constantPoolGen).startsWith(
169: "jdo")) {
170: String key = is.getClassName(constantPoolGen)
171: + "|"
172: + is.getFieldName(constantPoolGen);
173: if (fieldToEnhanceMap.containsKey(key)) {
174: SwapFieldHelper helper = (SwapFieldHelper) fieldToEnhanceMap
175: .get(key);
176: // replace it with our static replacement method
177: ih
178: .setInstruction(instructionFactory
179: .createInvoke(
180: helper.className,
181: helper.jdoGetName,
182: helper.type,
183: new Type[] { new ObjectType(
184: helper.className) },
185: Constants.INVOKESTATIC));
186: il.setPositions();
187: il.update();
188:
189: changed = true;
190: InstructionHandle prevIhDUP = ih.getPrev();
191: Instruction iffyDup = prevIhDUP
192: .getInstruction();
193: if (iffyDup.getClass().getName().equals(
194: D_U_P)) { // The previos ist was a DUP
195: ih = ih.getPrev();
196: InstructionHandle prevIhALOAD = ih
197: .getPrev();
198: Instruction iffyAload = prevIhALOAD
199: .getInstruction();
200: if (iffyAload.getClass().getName()
201: .equals(A_LOAD)) { // The inst before that was a ALOAD
202: ALOAD aLoad = (ALOAD) iffyAload;
203: ih.setInstruction(aLoad); // Swap ref out
204: il.setPositions();
205: il.update();
206: } else {
207: ih = ih.getNext();
208: }
209: }
210: }
211: }
212: } else if (ins.getClass().getName().equals(PUT_FIELD)) {
213: PUTFIELD is = (PUTFIELD) ins;
214: if (!is.getFieldName(constantPoolGen).startsWith(
215: "jdo")) {
216: String key = is.getClassName(constantPoolGen)
217: + "|"
218: + is.getFieldName(constantPoolGen);
219: if (fieldToEnhanceMap.containsKey(key)) {
220: SwapFieldHelper helper = (SwapFieldHelper) fieldToEnhanceMap
221: .get(key);
222: // replace it with our static replacement method
223: ih
224: .setInstruction(instructionFactory
225: .createInvoke(
226: helper.className,
227: helper.jdoSetName,
228: Type.VOID,
229: new Type[] {
230: new ObjectType(
231: helper.className),
232: helper.type },
233: Constants.INVOKESTATIC));
234: il.setPositions();
235: il.update();
236: changed = true;
237: }
238: }
239: }
240: // next instruction
241: ih = ih.getNext();
242: }
243: if (changed) {
244: hasChanges = true;
245: il.setPositions();
246: il.update();
247: mg.setMaxLocals();
248: mg.setMaxStack();
249: Method method = mg.getMethod();
250: classGen.replaceMethod(m, method);
251: }
252: }
253: }
254: }
|