1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.replacements.classfile; 24 25 import java.io.ByteArrayInputStream; 26 import java.io.DataInputStream; 27 import java.io.IOException; 28 import java.lang.instrument.Instrumentation; 29 30 import org.graalvm.compiler.bytecode.Bytecode; 31 import org.graalvm.compiler.debug.GraalError; 32 import org.graalvm.compiler.replacements.classfile.ClassfileConstant.Utf8; 33 34 import jdk.vm.ci.meta.ConstantPool; 35 import jdk.vm.ci.meta.DefaultProfilingInfo; 36 import jdk.vm.ci.meta.ExceptionHandler; 37 import jdk.vm.ci.meta.JavaType; 38 import jdk.vm.ci.meta.LineNumberTable; 39 import jdk.vm.ci.meta.Local; 40 import jdk.vm.ci.meta.LocalVariableTable; 41 import jdk.vm.ci.meta.ProfilingInfo; 42 import jdk.vm.ci.meta.ResolvedJavaMethod; 43 import jdk.vm.ci.meta.TriState; 44 45 /** 46 * The bytecode properties of a method as parsed directly from a class file without any 47 * {@linkplain Instrumentation instrumentation} or other rewriting performed on the bytecode. 48 */ 49 public class ClassfileBytecode implements Bytecode { 50 51 private static final int EXCEPTION_HANDLER_TABLE_SIZE_IN_BYTES = 8; 52 private static final int LINE_NUMBER_TABLE_ENTRY_SIZE_IN_BYTES = 4; 53 private static final int LOCAL_VARIABLE_TABLE_SIZE_IN_BYTES = 10; 54 55 private final ResolvedJavaMethod method; 56 57 private final ClassfileConstantPool constantPool; 58 59 private byte[] code; 60 private int maxLocals; 61 private int maxStack; 62 63 private byte[] exceptionTableBytes; 64 private byte[] lineNumberTableBytes; 65 private byte[] localVariableTableBytes; 66 67 public ClassfileBytecode(ResolvedJavaMethod method, DataInputStream stream, ClassfileConstantPool constantPool) throws IOException { 68 this.method = method; 69 this.constantPool = constantPool; 70 maxStack = stream.readUnsignedShort(); 71 maxLocals = stream.readUnsignedShort(); 72 int codeLength = stream.readInt(); 73 code = new byte[codeLength]; 74 stream.readFully(code); 75 int exceptionTableLength = stream.readUnsignedShort(); 76 exceptionTableBytes = new byte[exceptionTableLength * EXCEPTION_HANDLER_TABLE_SIZE_IN_BYTES]; 77 stream.readFully(exceptionTableBytes); 78 readCodeAttributes(stream); 79 } 80 81 private void readCodeAttributes(DataInputStream stream) throws IOException { 82 int count = stream.readUnsignedShort(); 83 for (int i = 0; i < count; i++) { 84 String attributeName = constantPool.get(Utf8.class, stream.readUnsignedShort()).value; 85 int attributeLength = stream.readInt(); 86 switch (attributeName) { 87 case "LocalVariableTable": { 88 int length = stream.readUnsignedShort(); 89 localVariableTableBytes = new byte[length * LOCAL_VARIABLE_TABLE_SIZE_IN_BYTES]; 90 stream.readFully(localVariableTableBytes); 91 break; 92 } 93 case "LineNumberTable": { 94 int length = stream.readUnsignedShort(); 95 lineNumberTableBytes = new byte[length * LINE_NUMBER_TABLE_ENTRY_SIZE_IN_BYTES]; 96 stream.readFully(lineNumberTableBytes); 97 break; 98 } 99 default: { 100 Classfile.skipFully(stream, attributeLength); 101 break; 102 } 103 } 104 } 105 } 106 107 @Override 108 public byte[] getCode() { 109 return code; 110 } 111 112 @Override 113 public int getCodeSize() { 114 return code.length; 115 } 116 117 @Override 118 public int getMaxLocals() { 119 return maxLocals; 120 } 121 122 @Override 123 public int getMaxStackSize() { 124 return maxStack; 125 } 126 127 @Override 128 public ExceptionHandler[] getExceptionHandlers() { 129 if (exceptionTableBytes == null) { 130 return new ExceptionHandler[0]; 131 } 132 133 final int exceptionTableLength = exceptionTableBytes.length / EXCEPTION_HANDLER_TABLE_SIZE_IN_BYTES; 134 ExceptionHandler[] handlers = new ExceptionHandler[exceptionTableLength]; 135 DataInputStream stream = new DataInputStream(new ByteArrayInputStream(exceptionTableBytes)); 136 137 for (int i = 0; i < exceptionTableLength; i++) { 138 try { 139 final int startPc = stream.readUnsignedShort(); 140 final int endPc = stream.readUnsignedShort(); 141 final int handlerPc = stream.readUnsignedShort(); 142 int catchTypeIndex = stream.readUnsignedShort(); 143 144 JavaType catchType; 145 if (catchTypeIndex == 0) { 146 catchType = null; 147 } else { 148 final int opcode = -1; // opcode is not used 149 catchType = constantPool.lookupType(catchTypeIndex, opcode); 150 151 // Check for Throwable which catches everything. 152 if (catchType.toJavaName().equals("java.lang.Throwable")) { 153 catchTypeIndex = 0; 154 catchType = null; 155 } 156 } 157 handlers[i] = new ExceptionHandler(startPc, endPc, handlerPc, catchTypeIndex, catchType); 158 } catch (IOException e) { 159 throw new GraalError(e); 160 } 161 } 162 163 return handlers; 164 } 165 166 @Override 167 public StackTraceElement asStackTraceElement(int bci) { 168 int line = getLineNumberTable().getLineNumber(bci); 169 return new StackTraceElement(method.getDeclaringClass().toJavaName(), method.getName(), method.getDeclaringClass().getSourceFileName(), line); 170 } 171 172 @Override 173 public ConstantPool getConstantPool() { 174 return constantPool; 175 } 176 177 @Override 178 public LineNumberTable getLineNumberTable() { 179 if (lineNumberTableBytes == null) { 180 return null; 181 } 182 183 final int lineNumberTableLength = lineNumberTableBytes.length / LINE_NUMBER_TABLE_ENTRY_SIZE_IN_BYTES; 184 DataInputStream stream = new DataInputStream(new ByteArrayInputStream(lineNumberTableBytes)); 185 int[] bci = new int[lineNumberTableLength]; 186 int[] line = new int[lineNumberTableLength]; 187 188 for (int i = 0; i < lineNumberTableLength; i++) { 189 try { 190 bci[i] = stream.readUnsignedShort(); 191 line[i] = stream.readUnsignedShort(); 192 } catch (IOException e) { 193 throw new GraalError(e); 194 } 195 } 196 197 return new LineNumberTable(line, bci); 198 } 199 200 @Override 201 public LocalVariableTable getLocalVariableTable() { 202 if (localVariableTableBytes == null) { 203 return null; 204 } 205 206 final int localVariableTableLength = localVariableTableBytes.length / LOCAL_VARIABLE_TABLE_SIZE_IN_BYTES; 207 DataInputStream stream = new DataInputStream(new ByteArrayInputStream(localVariableTableBytes)); 208 Local[] locals = new Local[localVariableTableLength]; 209 210 for (int i = 0; i < localVariableTableLength; i++) { 211 try { 212 final int startBci = stream.readUnsignedShort(); 213 final int endBci = startBci + stream.readUnsignedShort(); 214 final int nameCpIndex = stream.readUnsignedShort(); 215 final int typeCpIndex = stream.readUnsignedShort(); 216 final int slot = stream.readUnsignedShort(); 217 218 String localName = constantPool.lookupUtf8(nameCpIndex); 219 String localType = constantPool.lookupUtf8(typeCpIndex); 220 221 ClassfileBytecodeProvider context = constantPool.context; 222 Class<?> c = context.resolveToClass(localType); 223 locals[i] = new Local(localName, context.metaAccess.lookupJavaType(c), startBci, endBci, slot); 224 } catch (IOException e) { 225 throw new GraalError(e); 226 } 227 } 228 229 return new LocalVariableTable(locals); 230 } 231 232 @Override 233 public ResolvedJavaMethod getMethod() { 234 return method; 235 } 236 237 @Override 238 public ProfilingInfo getProfilingInfo() { 239 return DefaultProfilingInfo.get(TriState.FALSE); 240 } 241 242 @Override 243 public String toString() { 244 return getClass().getName() + method.format("<%H.%n(%p)>"); 245 } 246 }