/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, the * "License"). You may not use this file except in compliance with the * License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * Contributor(s): * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun * Microsystems, Inc. All Rights Reserved. * * If you wish your version of this file to be governed by only the CDDL * or only the GPL Version 2, indicate your decision by adding * "[Contributor] elects to include this software in this distribution * under the [CDDL or GPL Version 2] license." If you do not indicate a * single choice of license, a recipient has the option to distribute * your version of this file under either the CDDL, the GPL Version 2 or * to extend the choice of license to its licensees as provided above. * However, if you add GPL Version 2 code and therefore, elected the GPL * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ package org.netbeans.modules.scala.debugger.projects; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.LineMap; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; import com.sun.source.util.SourcePositions; import com.sun.source.util.TreePath; import com.sun.source.util.Trees; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; import org.netbeans.api.java.source.CompilationController; import org.netbeans.api.java.source.ElementUtilities; import org.netbeans.spi.debugger.jpda.EditorContext; import org.openide.ErrorManager; /** * This class tries to match the AST expression to bytecode. The result * of this match is provided as an * {@link org.netbeans.spi.debugger.jpda.EditorContext.ExpressionTree} object. * * @author Martin Entlicher */ class AST2Bytecode { /** Creates a new instance of AST2Bytecode */ private AST2Bytecode() { } static EditorContext.Operation[] matchSourceTree2Bytecode( CompilationUnitTree cu, CompilationController ci, List<Tree> treeNodes, ExpressionScanner.ExpressionsInfo info, byte[] bytecodes, int[] indexes, byte[] constantPool, OperationCreationDelegate opCreationDelegate, Map<Tree, EditorContext.Operation> nodeOperations) { Trees trees = ci.getTrees(); Types types = ci.getTypes(); SourcePositions sp = trees.getSourcePositions(); //List<Tree> treeNodes = linearizeTree(expTrees); if (treeNodes == null) return null; if (indexes == null) return null; int length = treeNodes.size(); List<EditorContext.Operation> operations = new ArrayList<EditorContext.Operation>(length); LineMap lineMap = cu.getLineMap(); int indexesIndex = 0; int from = indexes[indexesIndex]; int to = indexes[indexesIndex + 1]; for (int treeIndex = 0; treeIndex < length; treeIndex++) { Tree node = treeNodes.get(treeIndex); Tree.Kind kind = node.getKind(); EditorContext.Operation op = null; if (kind.equals(Tree.Kind.METHOD_INVOCATION) || kind.equals(Tree.Kind.NEW_CLASS)) { int opcode; do { do { opcode = bytecodes[from] & 0xFF; if (isMethodCall(opcode)) { break; } from += getInstrSize(opcode, bytecodes, from); } while (from < to); if (from < to) { break; } if ((indexesIndex + 2) < indexes.length) { indexesIndex += 2; from = indexes[indexesIndex]; to = indexes[indexesIndex + 1]; } else { break; } } while (true); if (from < to) { // We have the method call if (!ci.getTreeUtilities().isSynthetic(ci.getTrees().getPath(cu, node))) { int pos = (int) sp.getStartPosition(cu, node); EditorContext.Position startPosition = opCreationDelegate.createPosition( pos, (int) lineMap.getLineNumber(pos), (int) lineMap.getColumnNumber(pos) ); pos = (int) sp.getEndPosition(cu, node); EditorContext.Position endPosition = opCreationDelegate.createPosition( pos, (int) lineMap.getLineNumber(pos), (int) lineMap.getColumnNumber(pos) ); Tree identifier; String methodName; String methodClassType; boolean getStartPosFromMethodLength = false; if (kind.equals(Tree.Kind.NEW_CLASS)) { identifier = ((NewClassTree) node).getIdentifier(); methodName = "<init>"; TreePath iPath = TreePath.getPath(cu, identifier); TypeMirror type = trees.getTypeMirror(iPath); if (type.getKind() == TypeKind.ERROR) { // There are errors, give it up. return null; } assert type.getKind() == TypeKind.DECLARED; TypeElement te = (TypeElement) types.asElement(type); methodClassType = ElementUtilities.getBinaryName(te); } else { //identifier = ((MemberSelectTree) ((MethodInvocationTree) node).getMethodSelect()).getIdentifier(); identifier = ((MethodInvocationTree) node).getMethodSelect(); if (identifier.getKind() == Tree.Kind.IDENTIFIER) { methodName = ((IdentifierTree) identifier).getName().toString(); TreePath iPath = TreePath.getPath(cu, identifier); TypeElement te = trees.getScope(iPath).getEnclosingClass(); if (te == null) { // No enclosing class? Some error, give it up. return null; } methodClassType = ElementUtilities.getBinaryName(te); } else { methodName = ((MemberSelectTree) identifier).getIdentifier().toString(); getStartPosFromMethodLength = true; ExpressionTree exp = ((MemberSelectTree) identifier).getExpression(); TreePath expPath = TreePath.getPath(cu, exp); TypeMirror type = trees.getTypeMirror(expPath); if (type.getKind() == TypeKind.ERROR) { // There are errors, give it up. return null; } TypeElement te; if (type.getKind() == TypeKind.DECLARED) { te = (TypeElement) types.asElement(type); } else if (type.getKind() == TypeKind.TYPEVAR) { TypeParameterElement tpe = (TypeParameterElement) types.asElement(type); List<? extends TypeMirror> exts = tpe.getBounds(); if (exts.size() == 1) { type = exts.get(0); if (type.getKind() == TypeKind.DECLARED) { te = (TypeElement) types.asElement(type); } else { return null; // Unsupported } } else { return null; // Unsupported } } else { ErrorManager.getDefault().notify(new IllegalStateException("Unexpected type "+type+" in "+treeNodes)); return null; } methodClassType = ElementUtilities.getBinaryName(te); } } pos = (int) sp.getEndPosition(cu, identifier); EditorContext.Position methodEndPosition = opCreationDelegate.createPosition( pos, (int) lineMap.getLineNumber(pos), (int) lineMap.getColumnNumber(pos) ); if (getStartPosFromMethodLength) { pos = pos - methodName.length(); } else { pos = (int) sp.getStartPosition(cu, identifier); } EditorContext.Position methodStartPosition = opCreationDelegate.createPosition( pos, (int) lineMap.getLineNumber(pos), (int) lineMap.getColumnNumber(pos) ); /* EditorContext.Operation op = opCreationDelegate.createOperation( startPosition, endPosition, from ); */ op = opCreationDelegate.createMethodOperation( startPosition, endPosition, methodStartPosition, methodEndPosition, methodName, methodClassType, from ); //treeNodes.get(treeIndex).setCodeIndex(from); operations.add(op); } from += getInstrSize(opcode, bytecodes, from); } else { return null; // Mismatch } } if (op != null) { nodeOperations.put(node, op); } } // Check the rest of the bytecode for method calls: do { while (from < to) { int opcode = bytecodes[(int) from] & 0xFF; if (isMethodCall(opcode)) { return null; // Mismatch } from += getInstrSize(opcode, bytecodes, from); } if ((indexesIndex + 2) < indexes.length) { indexesIndex += 2; from = indexes[indexesIndex]; to = indexes[indexesIndex + 1]; } else { break; } } while (true); /* // Assign next operations: for (int treeIndex = 0; treeIndex < length; treeIndex++) { Tree node = treeNodes.get(treeIndex); Set<Tree> nextNodes = info.getNextExpressions(node); if (nextNodes != null) { EditorContext.Operation op = nodeOperations.get(node); if (op == null) { for (int backIndex = treeIndex - 1; backIndex >= 0; backIndex--) { node = treeNodes.get(backIndex); op = nodeOperations.get(node); if (op != null) break; } } if (op != null) { assignNext(op, opCreationDelegate, info, nodeOperations, nextNodes, treeNodes); } } } */ return operations.toArray(new EditorContext.Operation[] {}); } /*private static void assignNext(EditorContext.Operation op, OperationCreationDelegate opCreationDelegate, ExpressionScanner.ExpressionsInfo info, Map<Tree, EditorContext.Operation> nodeOperations, Set<Tree> nextNodes, List<Tree> allNodes) { for (Tree t : nextNodes) { EditorContext.Operation nextOp = nodeOperations.get(t); if (nextOp != null) { opCreationDelegate.addNextOperationTo(op, nextOp); } else { Set<Tree> nextNextNodes = info.getNextExpressions(t); if (nextNextNodes == null) { boolean check = false; for (int treeIndex = 0; treeIndex < allNodes.size(); treeIndex++) { Tree node = allNodes.get(treeIndex); if (check) { nextNextNodes = info.getNextExpressions(node); if (nextNextNodes != null) break; } else { if (t == node) { check = true; } } } } else { assignNext(op, opCreationDelegate, info, nodeOperations, nextNextNodes, allNodes); } } } }*/ private static boolean isMethodCall(int opcode) { return opcode >= 182 && opcode <= 185; } private static int getInstrSize(int opcode, byte[] bytecodes, long codeIndex) { if (opcode <= 15) return 1; // nop - dconst_1 if (opcode == 16) return 2; // bipush <byte> if (opcode == 17) return 3; // sipush <byte1> <byte2> if (opcode == 18) return 2; // ldc <index> if (opcode <= 20) return 3; // ldc_w <byte1> <byte2>, ldc2_w <byte1> <byte2> if (opcode <= 25) return 2; // iload <index>, lload <index>, fload <index>, dload <index>, aload <index> if (opcode <= 53) return 1; // <x>load_<n>, <x>aload if (opcode <= 58) return 2; // <x>store <index> if (opcode <= 86) return 1; // <x>store_<n>, <x>astore if (opcode <= 94) return 1; // pop*, dup* if (opcode <= 131) return 1; // swap, <x>add, <x>sub, <x>mul, <x>div, <x>rem, <x>neg, *sh*, <x>and, <x>or, <x>xor if (opcode <= 132) return 3; // iinc <index> <const> if (opcode <= 147) return 1; // <x>2<x> if (opcode <= 152) return 1; // <x>cmp<x> if (opcode <= 168) return 3; // if<cond> <branchbyte1> <branchbyte2>, goto <branchbyte1> <branchbyte2>, jsr <branchbyte1> <branchbyte2> if (opcode <= 169) return 2; // ret <index> if (opcode == 170) return tableswitchSize(bytecodes, codeIndex); if (opcode == 171) return lookupswitchSize(bytecodes, codeIndex); if (opcode <= 177) return 1; // <x>return if (opcode <= 184) return 3; // <get/put><static/field> <byte1> <byte2>, invokevirtual, invokespecial, invokestatic if (opcode == 185) return 5; // invokeinterface // 186 not used if (opcode == 187) return 3; // new <byte1> <byte2> if (opcode == 188) return 2; // newarray <atype> if (opcode == 189) return 3; // anewarray <byte1> <byte2> if (opcode <= 191) return 1; // arraylength, athrow if (opcode <= 193) return 3; // checkcast <byte1> <byte2>, instanceof <byte1> <byte2> if (opcode <= 195) return 1; // monitorenter, monitorexit if (opcode == 196) return wideSize(bytecodes, codeIndex); if (opcode == 197) return 4; // multianewarray if (opcode <= 199) return 3; // ifnull/ifnonnull <byte1> <byte2> if (opcode <= 201) return 5; // goto_w/jsr_w 4x<byte> return 1; // reserved opcodes } private static int tableswitchSize(byte[] bytecodes, long codeIndex) { int padding = 4 - ((int) codeIndex % 4); // Following byte begins at an address that is a multiple of 4 int pos = (int) codeIndex + padding; pos += 4; // default int low = readInt(bytecodes, pos); pos += 4; // low int high = readInt(bytecodes, pos); pos += 4; // high pos += (high - low + 1) << 2; // high - low + 1 32-bit offsets return pos - (int) codeIndex; } private static int lookupswitchSize(byte[] bytecodes, long codeIndex) { int padding = 4 - ((int) codeIndex % 4); // Following byte begins at an address that is a multiple of 4 int pos = (int) codeIndex + padding; pos += 4; // default int npairs = readInt(bytecodes, pos); pos += 4; // npairs pos += npairs << 3; // npairs 2x32-bit numbers (pairs) return pos - (int) codeIndex; } private static int wideSize(byte[] bytecodes, long codeIndex) { int opcode = bytecodes[(int) codeIndex + 1] & 0xFF; if (opcode == 132) { // iinc return 6; } else { return 4; } } private static int readUnsignedShort(byte[] bytecodes, int pos) { return ((bytecodes[pos] & 0xFF) << 8) | (bytecodes[pos + 1] & 0xFF); } private static int readInt(byte[] bytecodes, int pos) { return (bytecodes[pos ] & 0xFF) << 24 | (bytecodes[pos + 1] & 0xFF) << 16 | (bytecodes[pos + 2] & 0xFF) << 8 | (bytecodes[pos + 3] & 0xFF); } static interface OperationCreationDelegate { /* EditorContext.Operation createOperation( EditorContext.Position startPosition, EditorContext.Position endPosition, int bytecodeIndex); */ EditorContext.Operation createMethodOperation( EditorContext.Position startPosition, EditorContext.Position endPosition, EditorContext.Position methodStartPosition, EditorContext.Position methodEndPosition, String methodName, String methodClassType, int bytecodeIndex); EditorContext.Position createPosition(int offset, int line, int column); void addNextOperationTo(EditorContext.Operation operation, EditorContext.Operation next); } }