/* * Copyright 2000-2009 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.util.cls; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NonNls; public class ClsUtil { public static final int MAGIC = 0xCAFEBABE; public static final int CONSTANT_Class = 7; public static final int CONSTANT_Fieldref = 9; public static final int CONSTANT_Methodref = 10; public static final int CONSTANT_InterfaceMethodref = 11; public static final int CONSTANT_String = 8; public static final int CONSTANT_Integer = 3; public static final int CONSTANT_Float = 4; public static final int CONSTANT_Long = 5; public static final int CONSTANT_Double = 6; public static final int CONSTANT_NameAndType = 12; public static final int CONSTANT_Utf8 = 1; public static final int CONSTANT_MethodHandle = 15; public static final int CONSTANT_MethodType = 16; public static final int CONSTANT_InvokeDynamic = 18; public static final int ACC_PUBLIC = 0x0001; public static final int ACC_PRIVATE = 0x0002; public static final int ACC_PROTECTED = 0x0004; public static final int ACC_STATIC = 0x0008; public static final int ACC_FINAL = 0x0010; public static final int ACC_SYNCHRONIZED = 0x0020; public static final int ACC_BRIDGE = 0x0040; public static final int ACC_VARARGS = 0x0080; public static final int ACC_VOLATILE = 0x0040; public static final int ACC_TRANSIENT = 0x0080; public static final int ACC_NATIVE = 0x0100; public static final int ACC_INTERFACE = 0x0200; public static final int ACC_ABSTRACT = 0x0400; public static final int ACC_SYNTHETIC = 0x1000; public static final int ACC_ANNOTATION = 0x2000; public static final int ACC_ENUM = 0x4000; /** * Mask of access_flags that are meaningful in .class file format (VM Spec 4.1) */ public static final int ACC_CLASS_MASK = ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT | ACC_STATIC | ACC_ANNOTATION; public static final int ACC_FIELD_MASK = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT; public static final int ACC_METHOD_MASK = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT; private static final int POSITIVE_INFINITY_AS_INT = 0x7f800000; private static final long POSITIVE_INFINITY_AS_LONG = 0x7f80000000000000L; private static final int NEGATIVE_INFINITY_AS_INT = 0xff800000; private static final long NEGATIVE_INFINITY_AS_LONG = 0xff80000000000000L; public static int readU1(BytePointer ptr) throws ClsFormatException { if (ptr.offset < 0 || ptr.offset >= ptr.bytes.length) { throw new ClsFormatException(); } return ptr.bytes[ptr.offset++] & 0xFF; } public static int readU2(BytePointer ptr) throws ClsFormatException { int b1 = readU1(ptr); int b2 = readU1(ptr); return (b1 << 8) + b2; } public static int readU4(BytePointer ptr) throws ClsFormatException { int b1 = readU1(ptr); int b2 = readU1(ptr); int b3 = readU1(ptr); int b4 = readU1(ptr); return (b1 << 24) + (b2 << 16) + (b3 << 8) + b4; } public static long readU8(BytePointer ptr) throws ClsFormatException { long b1 = readU1(ptr); long b2 = readU1(ptr); long b3 = readU1(ptr); long b4 = readU1(ptr); long b5 = readU1(ptr); long b6 = readU1(ptr); long b7 = readU1(ptr); long b8 = readU1(ptr); return (b1 << 56) + (b2 << 48) + (b3 << 40) + (b4 << 32) + (b5 << 24) + (b6 << 16) + (b7 << 8) + b8; } public static String readUtf8Info(BytePointer ptr) throws ClsFormatException { return readUtf8Info(ptr, -1, -1); } public static String readUtf8Info(BytePointer ptr, int oldChar, int newChar) throws ClsFormatException { int tag = readU1(ptr); if (tag != CONSTANT_Utf8) { throw new ClsFormatException(); } int length = readU2(ptr); return readUtf8(ptr.bytes, ptr.offset, ptr.offset + length, oldChar, newChar); } public static String readUtf8Info(byte[] bytes, int startOffset) throws ClsFormatException { int offset = startOffset; if (offset + 3 > bytes.length) { throw new ClsFormatException(); } int tag = bytes[offset++] & 0xFF; if (tag != CONSTANT_Utf8) { throw new ClsFormatException(); } int b1 = bytes[offset++] & 0xFF; int b2 = bytes[offset++] & 0xFF; int length = (b1 << 8) + b2; if (offset + length > bytes.length) { throw new ClsFormatException(); } return readUtf8(bytes, offset, offset + length); } private static String readUtf8(byte[] bytes, int startOffset, int endOffset) throws ClsFormatException { return readUtf8(bytes, startOffset, endOffset, -1, -1); } private static String readUtf8(byte[] bytes, int startOffset, int endOffset, int oldChar, int newChar) throws ClsFormatException { char[] buffer = new char[endOffset - startOffset]; int bOffset = 0; int offset = startOffset; while (offset < endOffset) { int b = bytes[offset++] & 0xFF; if (b == 0 || b >= 0xF0) { throw new ClsFormatException(); } if (b < 0x80) { buffer[bOffset++] = (char) (b == oldChar ? newChar : b); } else { if (offset == endOffset) { throw new ClsFormatException(); } int b1 = bytes[offset++] & 0xFF; if ((b & 0x20) == 0) { buffer[bOffset++] = (char) (((b & 0x1F) << 6) + (b1 & 0x3F)); } else { if (offset == endOffset) { throw new ClsFormatException(); } int b2 = bytes[offset++] & 0xFF; buffer[bOffset++] = (char) (((b & 0xF) << 12) + ((b1 & 0x3F) << 6) + (b2 & 0x3F)); } } } return new String(buffer, 0, bOffset); } public static double readDouble(BytePointer ptr) throws ClsFormatException { int high = readU4(ptr); int low = readU4(ptr); long longValue = ((long) high << 32) + low; double doubleValue; if (longValue == POSITIVE_INFINITY_AS_LONG) { doubleValue = Double.POSITIVE_INFINITY; } else if (longValue == NEGATIVE_INFINITY_AS_LONG) { doubleValue = Double.NEGATIVE_INFINITY; } else if (0x7ff0000000000001L <= longValue && longValue <= 0x7fffffffffffffffL) { doubleValue = Double.NaN; } else if (0xfff0000000000001L <= longValue && longValue <= 0xffffffffffffffffL) { doubleValue = Double.NaN; } else { int s = ((longValue >> 63) == 0) ? 1 : -1; int e = (int)((longValue >> 52) & 0x7ffL); long m = (e == 0) ? ((longValue & 0xfffffffffffffL) << 1) : ((longValue & 0xfffffffffffffL) | 0x10000000000000L); doubleValue = s * m * Math.pow(2, e - 1075); } return doubleValue; } public static float readFloat(BytePointer ptr) throws ClsFormatException { int value = readU4(ptr); float floatValue; if (value == POSITIVE_INFINITY_AS_INT) { floatValue = Float.POSITIVE_INFINITY; } else if (value == NEGATIVE_INFINITY_AS_INT) { floatValue = Float.NEGATIVE_INFINITY; } else if ((value >= 0x7f800001 && value <= 0x7fffffff) || (value >= 0xff800001 && value <= 0xffffffff)) { floatValue = Float.NaN; } else { int s = ((value >> 31) == 0) ? 1 : -1; int e = ((value >> 23) & 0xff); int m = (e == 0) ? (value & 0x7fffff) << 1 : (value & 0x7fffff) | 0x800000; floatValue = (float) (s * m * Math.pow(2, e - 150)); } return floatValue; } public static void skipAttribute(BytePointer ptr) throws ClsFormatException { ptr.offset += 2; int length = readU4(ptr); ptr.offset += length; } public static void skipAttributes(BytePointer ptr) throws ClsFormatException { int count = readU2(ptr); for (int i = 0; i < count; i++) { skipAttribute(ptr); } } @SuppressWarnings({"HardCodedStringLiteral"}) public static String getTypeText(byte[] data, int offset) throws ClsFormatException { int count = 0; while (true) { if (offset >= data.length) { throw new ClsFormatException(); } if (data[offset] != '[') break; offset++; count++; } String text; switch ((char) data[offset]) { default: throw new ClsFormatException(); case 'B': text = "byte"; break; case 'C': text = "char"; break; case 'D': text = "double"; break; case 'F': text = "float"; break; case 'I': text = "int"; break; case 'J': text = "long"; break; case 'S': text = "short"; break; case 'Z': text = "boolean"; break; case 'V': text = "void"; break; case 'L': int offset1 = offset + 1; while (true) { if (offset1 >= data.length) { throw new ClsFormatException(); } if (data[offset1] == ';') break; offset1++; } String className = readUtf8(data, offset + 1, offset1, '/', '.'); text = convertClassName(className, false); break; } for (int i = 0; i < count; i++) { text += "[]"; } return text; } public static int getTypeEndOffset(byte[] data, int startOffset) throws ClsFormatException { int offset = startOffset; while (true) { if (offset >= data.length) { throw new ClsFormatException(); } if (data[offset] != '[') break; offset++; } if (data[offset++] == 'L') { while (true) { if (offset >= data.length) { throw new ClsFormatException(); } if (data[offset++] == ';') break; } } return offset; } public static String convertClassName(String internalClassName, boolean convertSlashesToDots) { String className = convertSlashesToDots ? internalClassName.replace('/', '.') : internalClassName; int index = className.indexOf('$'); if (index < 0) return className; StringBuilder buffer = new StringBuilder(className); while(true){ if (className.length() == index + 1) break; char c = className.charAt(index + 1); if (Character.isJavaIdentifierStart(c)){ buffer.setCharAt(index, '.'); } index = className.indexOf('$', index + 1); if (index < 0) break; } return buffer.toString(); } public static boolean isPublic(int flags) { return (ACC_PUBLIC & flags) != 0; } public static boolean isProtected(int flags) { return (ACC_PROTECTED & flags) != 0; } public static boolean isPackageLocal(int flags) { return !isPublic(flags) && !isProtected(flags) && !isPrivate(flags); } public static boolean isPrivate(int flags) { return (ACC_PRIVATE & flags) != 0; } public static boolean isAbstract(int flags) { return (ACC_ABSTRACT & flags) != 0; } public static boolean isBridge(int flags) { return (ACC_BRIDGE & flags) != 0; } public static boolean isSynthetic(int flags) { return (ACC_SYNTHETIC & flags) != 0; } public static boolean isAnnotation(int flags) { return (ACC_ANNOTATION & flags) != 0; } public static boolean isFinal(int flags) { return (ACC_FINAL & flags) != 0; } public static boolean isStatic(int flags) { return (ACC_STATIC & flags) != 0; } public static String literalToString(CharSequence value, char quote) { int length = value.length(); @NonNls StringBuilder buffer = new StringBuilder(length + 3); buffer.append(quote); for (int i = 0; i < length; i++) { char c = value.charAt(i); if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') { buffer.append(c); continue; } switch (c) { case '\b': buffer.append("\\b"); break; case '\t': buffer.append("\\t"); break; case '\n': buffer.append("\\n"); break; case '\f': buffer.append("\\f"); break; case '\r': buffer.append("\\r"); break; case '\\': buffer.append("\\\\"); break; default: if (c == quote) { buffer.append("\\").append(quote); } else if (Character.isISOControl(c)) { String hexCode = StringUtil.toUpperCase(Integer.toHexString(c)); buffer.append("\\u"); int paddingCount = 4 - hexCode.length(); while (paddingCount-- > 0) { buffer.append(0); } buffer.append(hexCode); } else { buffer.append(c); } } } buffer.append(quote); return buffer.toString(); } }