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 24 25 package org.graalvm.compiler.replacements.classfile; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.DataInputStream; 29 import java.io.IOException; 30 31 import org.graalvm.compiler.bytecode.Bytecode; 32 import org.graalvm.compiler.bytecode.BytecodeProvider; 33 import org.graalvm.compiler.debug.GraalError; 34 import org.graalvm.compiler.replacements.classfile.ClassfileConstant.Utf8; 35 36 import jdk.vm.ci.meta.ConstantPool; 37 import jdk.vm.ci.meta.DefaultProfilingInfo; 38 import jdk.vm.ci.meta.ExceptionHandler; 39 import jdk.vm.ci.meta.JavaType; 40 import jdk.vm.ci.meta.LineNumberTable; 41 import jdk.vm.ci.meta.Local; 42 import jdk.vm.ci.meta.LocalVariableTable; 43 import jdk.vm.ci.meta.ProfilingInfo; 44 import jdk.vm.ci.meta.ResolvedJavaMethod; 45 import jdk.vm.ci.meta.TriState; 46 47 /** 48 * The bytecode properties of a method as parsed directly from a class file without any 49 * {@linkplain java.lang.instrument.Instrumentation instrumentation} or other rewriting performed on 50 * the bytecode. 51 */ 52 public class ClassfileBytecode implements Bytecode { 53 54 private static final int EXCEPTION_HANDLER_TABLE_SIZE_IN_BYTES = 8; 55 private static final int LINE_NUMBER_TABLE_ENTRY_SIZE_IN_BYTES = 4; 56 private static final int LOCAL_VARIABLE_TABLE_SIZE_IN_BYTES = 10; 57 58 private final ResolvedJavaMethod method; 59 60 private final ClassfileConstantPool constantPool; 61 62 private byte[] code; 63 private int maxLocals; 64 private int maxStack; 65 66 private byte[] exceptionTableBytes; 67 private byte[] lineNumberTableBytes; 68 private byte[] localVariableTableBytes; 69 ClassfileBytecode(ResolvedJavaMethod method, DataInputStream stream, ClassfileConstantPool constantPool)70 public ClassfileBytecode(ResolvedJavaMethod method, DataInputStream stream, ClassfileConstantPool constantPool) throws IOException { 71 this.method = method; 72 this.constantPool = constantPool; 73 maxStack = stream.readUnsignedShort(); 74 maxLocals = stream.readUnsignedShort(); 75 int codeLength = stream.readInt(); 76 code = new byte[codeLength]; 77 stream.readFully(code); 78 int exceptionTableLength = stream.readUnsignedShort(); 79 exceptionTableBytes = new byte[exceptionTableLength * EXCEPTION_HANDLER_TABLE_SIZE_IN_BYTES]; 80 stream.readFully(exceptionTableBytes); 81 readCodeAttributes(stream); 82 } 83 84 @Override getOrigin()85 public BytecodeProvider getOrigin() { 86 return constantPool.context; 87 } 88 readCodeAttributes(DataInputStream stream)89 private void readCodeAttributes(DataInputStream stream) throws IOException { 90 int count = stream.readUnsignedShort(); 91 for (int i = 0; i < count; i++) { 92 String attributeName = constantPool.get(Utf8.class, stream.readUnsignedShort()).value; 93 int attributeLength = stream.readInt(); 94 switch (attributeName) { 95 case "LocalVariableTable": { 96 int length = stream.readUnsignedShort(); 97 localVariableTableBytes = new byte[length * LOCAL_VARIABLE_TABLE_SIZE_IN_BYTES]; 98 stream.readFully(localVariableTableBytes); 99 break; 100 } 101 case "LineNumberTable": { 102 int length = stream.readUnsignedShort(); 103 lineNumberTableBytes = new byte[length * LINE_NUMBER_TABLE_ENTRY_SIZE_IN_BYTES]; 104 stream.readFully(lineNumberTableBytes); 105 break; 106 } 107 default: { 108 Classfile.skipFully(stream, attributeLength); 109 break; 110 } 111 } 112 } 113 } 114 115 @Override getCode()116 public byte[] getCode() { 117 return code; 118 } 119 120 @Override getCodeSize()121 public int getCodeSize() { 122 return code.length; 123 } 124 125 @Override getMaxLocals()126 public int getMaxLocals() { 127 return maxLocals; 128 } 129 130 @Override getMaxStackSize()131 public int getMaxStackSize() { 132 return maxStack; 133 } 134 135 @Override getExceptionHandlers()136 public ExceptionHandler[] getExceptionHandlers() { 137 if (exceptionTableBytes == null) { 138 return new ExceptionHandler[0]; 139 } 140 141 final int exceptionTableLength = exceptionTableBytes.length / EXCEPTION_HANDLER_TABLE_SIZE_IN_BYTES; 142 ExceptionHandler[] handlers = new ExceptionHandler[exceptionTableLength]; 143 DataInputStream stream = new DataInputStream(new ByteArrayInputStream(exceptionTableBytes)); 144 145 for (int i = 0; i < exceptionTableLength; i++) { 146 try { 147 final int startPc = stream.readUnsignedShort(); 148 final int endPc = stream.readUnsignedShort(); 149 final int handlerPc = stream.readUnsignedShort(); 150 int catchTypeIndex = stream.readUnsignedShort(); 151 152 JavaType catchType; 153 if (catchTypeIndex == 0) { 154 catchType = null; 155 } else { 156 final int opcode = -1; // opcode is not used 157 catchType = constantPool.lookupType(catchTypeIndex, opcode); 158 159 // Check for Throwable which catches everything. 160 if (catchType.toJavaName().equals("java.lang.Throwable")) { 161 catchTypeIndex = 0; 162 catchType = null; 163 } 164 } 165 handlers[i] = new ExceptionHandler(startPc, endPc, handlerPc, catchTypeIndex, catchType); 166 } catch (IOException e) { 167 throw new GraalError(e); 168 } 169 } 170 171 return handlers; 172 } 173 174 @Override asStackTraceElement(int bci)175 public StackTraceElement asStackTraceElement(int bci) { 176 int line = getLineNumberTable().getLineNumber(bci); 177 return new StackTraceElement(method.getDeclaringClass().toJavaName(), method.getName(), method.getDeclaringClass().getSourceFileName(), line); 178 } 179 180 @Override getConstantPool()181 public ConstantPool getConstantPool() { 182 return constantPool; 183 } 184 185 @Override getLineNumberTable()186 public LineNumberTable getLineNumberTable() { 187 if (lineNumberTableBytes == null) { 188 return null; 189 } 190 191 final int lineNumberTableLength = lineNumberTableBytes.length / LINE_NUMBER_TABLE_ENTRY_SIZE_IN_BYTES; 192 DataInputStream stream = new DataInputStream(new ByteArrayInputStream(lineNumberTableBytes)); 193 int[] bci = new int[lineNumberTableLength]; 194 int[] line = new int[lineNumberTableLength]; 195 196 for (int i = 0; i < lineNumberTableLength; i++) { 197 try { 198 bci[i] = stream.readUnsignedShort(); 199 line[i] = stream.readUnsignedShort(); 200 } catch (IOException e) { 201 throw new GraalError(e); 202 } 203 } 204 205 return new LineNumberTable(line, bci); 206 } 207 208 @Override getLocalVariableTable()209 public LocalVariableTable getLocalVariableTable() { 210 if (localVariableTableBytes == null) { 211 return null; 212 } 213 214 final int localVariableTableLength = localVariableTableBytes.length / LOCAL_VARIABLE_TABLE_SIZE_IN_BYTES; 215 DataInputStream stream = new DataInputStream(new ByteArrayInputStream(localVariableTableBytes)); 216 Local[] locals = new Local[localVariableTableLength]; 217 218 for (int i = 0; i < localVariableTableLength; i++) { 219 try { 220 final int startBci = stream.readUnsignedShort(); 221 final int endBci = startBci + stream.readUnsignedShort(); 222 final int nameCpIndex = stream.readUnsignedShort(); 223 final int typeCpIndex = stream.readUnsignedShort(); 224 final int slot = stream.readUnsignedShort(); 225 226 String localName = constantPool.lookupUtf8(nameCpIndex); 227 String localType = constantPool.lookupUtf8(typeCpIndex); 228 229 ClassfileBytecodeProvider context = constantPool.context; 230 Class<?> c = context.resolveToClass(localType); 231 locals[i] = new Local(localName, context.metaAccess.lookupJavaType(c), startBci, endBci, slot); 232 } catch (IOException e) { 233 throw new GraalError(e); 234 } 235 } 236 237 return new LocalVariableTable(locals); 238 } 239 240 @Override getMethod()241 public ResolvedJavaMethod getMethod() { 242 return method; 243 } 244 245 @Override getProfilingInfo()246 public ProfilingInfo getProfilingInfo() { 247 return DefaultProfilingInfo.get(TriState.FALSE); 248 } 249 250 @Override toString()251 public String toString() { 252 return getClass().getName() + method.format("<%H.%n(%p)>"); 253 } 254 } 255