001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.debugger.jpda;
043:
044: import com.sun.jdi.AbsentInformationException;
045: import com.sun.jdi.IncompatibleThreadStateException;
046: import com.sun.jdi.Location;
047: import com.sun.jdi.Method;
048: import com.sun.jdi.ReferenceType;
049: import com.sun.jdi.StackFrame;
050: import com.sun.jdi.ThreadReference;
051:
052: import java.lang.reflect.InvocationTargetException;
053: import java.util.ArrayList;
054: import java.util.HashMap;
055: import java.util.Iterator;
056: import java.util.List;
057: import java.util.Map;
058: import java.util.logging.Level;
059: import java.util.logging.Logger;
060:
061: import org.netbeans.api.debugger.DebuggerManager;
062: import org.netbeans.api.debugger.Session;
063: import org.netbeans.spi.debugger.jpda.EditorContext;
064: import org.netbeans.spi.debugger.jpda.EditorContext.Operation;
065:
066: /**
067: * The pool of operations, which are used for expression stepping.
068: *
069: * @author Martin Entlicher
070: */
071: public class ExpressionPool {
072:
073: private static final boolean IS_JDK_16 = !System.getProperty(
074: "java.version").startsWith("1.5"); // NOI18N
075: private static Logger logger = Logger
076: .getLogger("org.netbeans.modules.debugger.jpda.step"); // NOI18N
077:
078: private Map<ExpressionLocation, Expression> expressions = new HashMap<ExpressionLocation, Expression>();
079:
080: /**
081: * Creates a new instance of ExpressionPool
082: */
083: ExpressionPool() {
084: }
085:
086: public synchronized Expression getExpressionAt(Location loc,
087: String url) {
088: ExpressionLocation exprLocation = new ExpressionLocation(loc
089: .method(), loc.lineNumber());
090: if (!expressions.containsKey(exprLocation)) {
091: Expression expr = createExpressionAt(loc, url);
092: expressions.put(exprLocation, expr);
093: }
094: return expressions.get(exprLocation);
095: }
096:
097: // TODO: Clean unnecessray expressions:
098: /*
099: public synchronized void removeExpressionAt(Location loc) {
100: expressions.remove(new ExpressionLocation(loc.method(), loc.lineNumber()));
101: }
102: */
103: public void cleanUnusedExpressions(ThreadReference thr) {
104: synchronized (this ) {
105: if (expressions.size() == 0) {
106: return;
107: }
108: }
109: List<StackFrame> stackFrames;
110: try {
111: stackFrames = thr.frames();
112: synchronized (this ) {
113: for (Iterator<ExpressionLocation> locIt = expressions
114: .keySet().iterator(); locIt.hasNext();) {
115: ExpressionLocation exprLoc = locIt.next();
116: // TODO: Check the correct thread.
117: Method method = exprLoc.getMethod();
118: int line = exprLoc.getLine();
119: for (Iterator<StackFrame> it = stackFrames
120: .iterator(); it.hasNext();) {
121: StackFrame sf = it.next();
122: if (method.equals(sf.location().method())) {
123: //&& line == sf.location().lineNumber()) {
124: method = null;
125: break;
126: }
127: }
128: if (method != null) {
129: locIt.remove();
130: }
131: }
132: }
133: } catch (IncompatibleThreadStateException ex) {
134: // Ignore
135: }
136: }
137:
138: private Expression createExpressionAt(Location loc, String url) {
139: if (!loc.virtualMachine().canGetBytecodes()) {
140: // Can not analyze expressions without bytecode
141: return null;
142: }
143: ReferenceType clazzType = loc.declaringType();
144: final Method method = loc.method();
145: final byte[] bytecodes = method.bytecodes();
146: byte[] constantPool = new byte[0];
147: String JDKVersion = System.getProperty("java.version"); // NOI18N
148: if (IS_JDK_16) {
149: try { // => clazzType.constantPool(); on JDK 1.6.0 and higher
150: java.lang.reflect.Method constantPoolMethod = clazzType
151: .getClass().getMethod("constantPool",
152: new Class[0]); // NOI18N
153: try {
154: constantPool = (byte[]) constantPoolMethod.invoke(
155: clazzType, new Object[0]);
156: } catch (IllegalArgumentException ex) {
157: } catch (InvocationTargetException ex) {
158: } catch (IllegalAccessException ex) {
159: }
160: } catch (SecurityException ex) {
161: } catch (NoSuchMethodException ex) {
162: }
163: }
164: final byte[] theConstantPool = constantPool;
165: Session currentSession = DebuggerManager.getDebuggerManager()
166: .getCurrentSession();
167: final String language = currentSession == null ? null
168: : currentSession.getCurrentLanguage();
169:
170: int line = loc.lineNumber(language);
171:
172: final List<Location> methodLocations;
173: try {
174: methodLocations = method.allLineLocations(language, null);
175: } catch (AbsentInformationException aiex) {
176: logger.log(Level.FINE, aiex.getLocalizedMessage());
177: return null;
178: }
179:
180: Operation[] ops = EditorContextBridge.getContext()
181: .getOperations(url, line,
182: new EditorContext.BytecodeProvider() {
183: public byte[] constantPool() {
184: return theConstantPool;
185: }
186:
187: public byte[] byteCodes() {
188: return bytecodes;
189: }
190:
191: public int[] indexAtLines(int startLine,
192: int endLine) {
193: return getIndexesAtLines(
194: methodLocations, language,
195: startLine, endLine,
196: bytecodes.length);
197: }
198:
199: });
200: if (ops == null) {
201: logger.log(Level.FINE, "Unsuccessfull bytecode matching.");
202: return null;
203: }
204: if (ops.length == 0) { // No operations - do a line step instead
205: return null;
206: }
207: Location[] locations = new Location[ops.length];
208: for (int i = 0; i < ops.length; i++) {
209: int codeIndex = ops[i].getBytecodeIndex();
210: locations[i] = method.locationOfCodeIndex(codeIndex);
211: if (locations[i] == null) {
212: logger.log(Level.FINE,
213: "Location of the operation not found.");
214: return null;
215: }
216: }
217: Expression expr = new Expression(new ExpressionLocation(method,
218: line), ops, locations);
219: return expr;
220: }
221:
222: private static int[] getIndexesAtLines(List<Location> allLocations,
223: String language, int startLine, int endLine,
224: int methodEndIndex) {
225: Location startLocation;
226: int startlocline = 0;
227: int endlocline;
228: int firstLine = allLocations.get(0).lineNumber(language);
229: do {
230: startLocation = getLocationOfLine(allLocations, language,
231: startLine - startlocline++);
232: } while (startLocation == null
233: && (startLine - (startlocline - 1)) >= firstLine);
234: if (endLine > startLine - (startlocline - 1)) {
235: endlocline = 0;
236: } else {
237: endlocline = 1;
238: }
239: startLine -= (startlocline - 1);
240: endLine += endlocline;
241: List<int[]> indexes = new ArrayList<int[]>();
242: int startIndex = -1;
243: for (Location l : allLocations) {
244: int line = l.lineNumber(language);
245: if (startIndex == -1 && startLine <= line && line < endLine) {
246: startIndex = (int) l.codeIndex();
247: } else if (startIndex >= 0) {
248: indexes
249: .add(new int[] { startIndex,
250: (int) l.codeIndex() });
251: startIndex = -1;
252: }
253: }
254: if (indexes.size() == 0) {
255: if (startIndex >= 0) {
256: // End of the method
257: return new int[] { startIndex, methodEndIndex };
258: }
259: return null;
260: } else if (indexes.size() == 1) {
261: return indexes.get(0);
262: } else {
263: int[] arr = new int[2 * indexes.size()];
264: for (int i = 0; i < indexes.size(); i++) {
265: arr[2 * i] = indexes.get(i)[0];
266: arr[2 * i + 1] = indexes.get(i)[1];
267: }
268: return arr;
269: }
270: }
271:
272: private static Location getLocationOfLine(
273: List<Location> allLocations, String language, int line) {
274: for (Location l : allLocations) {
275: if (l.lineNumber(language) == line) {
276: return l;
277: }
278: }
279: return null;
280: }
281:
282: //private int[] singleIndexHolder = new int[1]; // Perf. optimization only
283:
284: public static final class Expression {
285:
286: private ExpressionLocation location;
287: private Operation[] operations;
288: private Location[] locations;
289:
290: Expression(ExpressionLocation location, Operation[] operations,
291: Location[] locations) {
292: this .location = location;
293: this .operations = operations;
294: this .locations = locations;
295: }
296:
297: public Operation[] getOperations() {
298: return operations;
299: }
300:
301: public Location[] getLocations() {
302: return locations;
303: }
304:
305: public int findNextOperationIndex(int codeIndex) {
306: for (int i = 0; i < operations.length; i++) {
307: int operationIndex = operations[i].getBytecodeIndex();
308: if (operationIndex > codeIndex) {
309: return i;
310: }
311: }
312: return -1;
313: }
314:
315: int[] findNextOperationIndexes(int codeIndex) {
316: for (int i = 0; i < operations.length; i++) {
317: int operationIndex = operations[i].getBytecodeIndex();
318: if (operationIndex == codeIndex) {
319: List<Operation> nextOperations = operations[i]
320: .getNextOperations();
321: if (!nextOperations.isEmpty()) {
322: int l = nextOperations.size();
323: int[] indexes = new int[l];
324: for (int ni = 0; ni < l; ni++) {
325: Operation op = nextOperations.get(ni);
326: int j;
327: for (j = 0; j < operations.length; j++) {
328: if (op == operations[j])
329: break;
330: }
331: if (j < operations.length) {
332: indexes[ni] = j;
333: } else {
334: indexes[ni] = -1;
335: }
336: }
337: return indexes;
338: }
339: }
340: if (operationIndex > codeIndex) {
341: return new int[] { i };
342: }
343: }
344: return null;
345: }
346:
347: OperationLocation[] findNextOperationLocations(int codeIndex) {
348: for (int i = 0; i < operations.length; i++) {
349: int operationIndex = operations[i].getBytecodeIndex();
350: if (operationIndex == codeIndex) {
351: List<Operation> nextOperations = operations[i]
352: .getNextOperations();
353: if (!nextOperations.isEmpty()) {
354: int l = nextOperations.size();
355: OperationLocation[] opLocations = new OperationLocation[l];
356: for (int ni = 0; ni < l; ni++) {
357: Operation op = nextOperations.get(ni);
358: int j;
359: for (j = 0; j < operations.length; j++) {
360: if (op == operations[j])
361: break;
362: }
363: if (j < operations.length) {
364: opLocations[ni] = //locations[j];
365: new OperationLocation(operations[j],
366: locations[j], j);
367: } else {
368: int ci = op.getBytecodeIndex();
369: Location loc = location.getMethod()
370: .locationOfCodeIndex(ci);
371: if (loc == null) {
372: logger
373: .log(Level.FINE,
374: "Location of the operation not found.");
375: return null;
376: }
377: opLocations[ni] = //loc;
378: new OperationLocation(op, loc, -1);
379: }
380: }
381: return opLocations;
382: }
383: }
384: if (operationIndex > codeIndex) {
385: return new OperationLocation[] { new OperationLocation(
386: operations[i], locations[i], i) };
387: }
388: }
389: return null;
390: }
391:
392: }
393:
394: public static final class ExpressionLocation {
395:
396: private Method method;
397: private int line;
398:
399: public ExpressionLocation(Method method, int line) {
400: this .method = method;
401: this .line = line;
402: }
403:
404: public Method getMethod() {
405: return method;
406: }
407:
408: public int getLine() {
409: return line;
410: }
411:
412: public boolean equals(Object obj) {
413: if (!(obj instanceof ExpressionLocation)) {
414: return false;
415: }
416: return ((ExpressionLocation) obj).line == line
417: && ((ExpressionLocation) obj).method.equals(method);
418: }
419:
420: public int hashCode() {
421: return method.hashCode() + line;
422: }
423:
424: }
425:
426: public static final class OperationLocation {
427:
428: private Operation op;
429: private Location loc;
430: private int index;
431:
432: OperationLocation(Operation op, Location loc, int index) {
433: this .op = op;
434: this .loc = loc;
435: this .index = index;
436: }
437:
438: public Operation getOperation() {
439: return op;
440: }
441:
442: public Location getLocation() {
443: return loc;
444: }
445:
446: public int getIndex() {
447: return index;
448: }
449:
450: }
451:
452: }
|