0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans;
0043:
0044: import java.io.UnsupportedEncodingException;
0045: import java.util.HashMap;
0046: import java.util.Iterator;
0047: import java.util.List;
0048: import org.openide.util.NbCollections;
0049:
0050: /** Class that can enhance bytecode with information about alternative
0051: * superclass and access modifiers. It can also extract this information
0052: * later when the class is about to be loaded into the VM.
0053: * <P>
0054: * The additional information is added to attributes of the classfile (global attributes
0055: * and also member attributes) and as such the class remain compatible and
0056: * understandable for any VM. But if loaded by classloader that before defining
0057: * the class invokes:
0058: * <pre>
0059: * byte[] arr = ...;
0060: * arr = PatchByteCode.patch (arr);
0061: * </pre>
0062: * The class is altered in its superclass and/or access modifiers.
0063: * <P>
0064: * The patching mechanism uses two attributes. ATTR_SUPERCLASS can be just
0065: * in global attributes pool (and only once), is of length 2 and contains index
0066: * into constant pool that contains definition of a Class that should become
0067: * the alternate superclass. Attribute ATTR_MEMBER can appear in global
0068: * attribute set and also in set of each member (field or method). It is of
0069: * length 2 and contains alternate value for access flags of the class or of
0070: * the field.
0071: * <P>
0072: * For purposes for speed, each patched class file has to end with bytes "nb".
0073: * This is achieved by finishing the patching process by adding third attribute
0074: * "org.netbeans.enhanced" with value "nb". As such the <code>PatchByteCode.patch</code>
0075: * can quickly check the byte array and process just those that need processing.
0076: *
0077: * @author Jaroslav Tulach
0078: */
0079: public final class PatchByteCode {
0080: private static final String ATTR_SUPERCLASS = "org.netbeans.superclass"; // NOI18N
0081: private static final byte[] BYTE_SUPERCLASS;
0082: static {
0083: try {
0084: BYTE_SUPERCLASS = ATTR_SUPERCLASS.getBytes("utf-8"); // NOI18N
0085: } catch (java.io.UnsupportedEncodingException ex) {
0086: throw new IllegalStateException(ex.getMessage());
0087: }
0088: }
0089: private static final String ATTR_INTERFACES = "org.netbeans.interfaces"; // NOI18N
0090: private static final byte[] BYTE_INTERFACES;
0091: static {
0092: try {
0093: BYTE_INTERFACES = ATTR_INTERFACES.getBytes("utf-8"); // NOI18N
0094: } catch (java.io.UnsupportedEncodingException ex) {
0095: throw new IllegalStateException(ex.getMessage());
0096: }
0097: }
0098: private static final String ATTR_MEMBER = "org.netbeans.member"; // NOI18N
0099: private static final byte[] BYTE_MEMBER;
0100: static {
0101: try {
0102: BYTE_MEMBER = ATTR_MEMBER.getBytes("utf-8"); // NOI18N
0103: } catch (java.io.UnsupportedEncodingException ex) {
0104: throw new IllegalStateException(ex.getMessage());
0105: }
0106: }
0107: private static final String ATTR_NAME = "org.netbeans.name"; // NOI18N
0108: private static final byte[] BYTE_NAME;
0109: static {
0110: try {
0111: BYTE_NAME = ATTR_NAME.getBytes("utf-8"); // NOI18N
0112: } catch (java.io.UnsupportedEncodingException ex) {
0113: throw new IllegalStateException(ex.getMessage());
0114: }
0115: }
0116:
0117: private static final String ATTR_INIT = "<init>"; // NOI18N
0118: private static final byte[] BYTE_INIT;
0119: static {
0120: try {
0121: BYTE_INIT = ATTR_INIT.getBytes("utf-8"); // NOI18N
0122: } catch (java.io.UnsupportedEncodingException ex) {
0123: throw new IllegalStateException(ex.getMessage());
0124: }
0125: }
0126:
0127: private static final String ATTR_INIT_TYPE = "()V"; // NOI18N
0128: private static final byte[] BYTE_INIT_TYPE;
0129: static {
0130: try {
0131: BYTE_INIT_TYPE = ATTR_INIT_TYPE.getBytes("utf-8"); // NOI18N
0132: } catch (java.io.UnsupportedEncodingException ex) {
0133: throw new IllegalStateException(ex.getMessage());
0134: }
0135: }
0136:
0137: private byte[] arr;
0138:
0139: private int cpCount;
0140: private int cpEnd;
0141: private int atCount;
0142: private int atEnd;
0143:
0144: /** the index of a string that matches the searched attribute */
0145: private int super ClassNameIndex;
0146: /** the possiton of found attribute */
0147: private int super ClassNameAttr;
0148: /** the index of a string that matches the searched attribute */
0149: private int interfacesNameIndex;
0150: /** the possiton of found attribute */
0151: private int interfacesNameAttr;
0152: /** the index of a string to patch members of a field */
0153: private int memberNameIndex = -1;
0154: /** position of attribute the change the access rights of the class */
0155: private int memberClassAttr = -1;
0156: /** index of <init> UTF8 in constant pool*/
0157: private int initIndex = -1;
0158: /** index of ()V UTF8 in constant pool */
0159: private int initIndexType = -1;
0160: /** index of CONSTANT_NameAndType index for <init> and ()V in pool */
0161: private int initNameTypeIndex = -1;
0162: /** position of the <init> method */
0163: private int initAttr = -1;
0164: /** index of string that identifies the rename of a member */
0165: private int renameNameIndex = -1;
0166:
0167: /** map that maps names of fields to their position in constant pool (String, int[1]) */
0168: private HashMap<String, int[]> nameIndexes;
0169:
0170: /** Creates a new instance of PatchByteCode
0171: *
0172: * @param nameIndexes hashmap from (String -> int[1])
0173: */
0174: private PatchByteCode(byte[] arr, HashMap<String, int[]> nameIndexes) {
0175: this .arr = arr;
0176: this .nameIndexes = nameIndexes;
0177:
0178: // scan twice because of back references
0179: scan();
0180: scan();
0181: }
0182:
0183: /** Generates patch attribute into the classfile to
0184: * allow method <code>patch</code> to modify the superclass of this
0185: * class.
0186: *
0187: * @param arr the bytecode to change
0188: * @param args map with arguments.
0189: * @return new version of the bytecode if changed, otherwise null to signal that
0190: * no change has been made
0191: */
0192: public static byte[] enhanceClass(byte[] arr,
0193: java.util.Map<String, Object> args) {
0194: if (isPatched(arr)) {
0195: // already patched
0196: return null;
0197: }
0198:
0199: String super Class = (String) args.get("netbeans.superclass");
0200: String interfaces = (String) args.get("netbeans.interfaces");
0201: List _methods = (List) args.get("netbeans.public");
0202: List<String> methods = _methods != null ? NbCollections
0203: .checkedListByCopy(_methods, String.class, true) : null;
0204: List _rename = (List) args.get("netbeans.rename");
0205: List<String> rename = _rename != null ? NbCollections
0206: .checkedListByCopy(_rename, String.class, true) : null;
0207:
0208: HashMap<String, int[]> m;
0209: if (methods != null || rename != null) {
0210: m = new HashMap<String, int[]>();
0211:
0212: if (methods != null) {
0213: for (String s : methods) {
0214: m.put(s, new int[1]);
0215: }
0216: }
0217:
0218: if (rename != null) {
0219: for (String s : rename) {
0220: m.put(s, new int[1]);
0221: }
0222: }
0223: } else {
0224: m = null;
0225: }
0226:
0227: PatchByteCode pc = new PatchByteCode(arr, m);
0228: boolean patched = false;
0229:
0230: if (super Class != null) {
0231: int x = pc.addClass(super Class);
0232:
0233: byte[] sup = new byte[2];
0234: writeU2(sup, 0, x);
0235: pc.addAttribute(ATTR_SUPERCLASS, sup);
0236:
0237: patched = true;
0238: }
0239:
0240: if (interfaces != null) {
0241: java.util.ArrayList<String> tokens = new java.util.ArrayList<String>();
0242: java.util.StringTokenizer tok = new java.util.StringTokenizer(
0243: interfaces, ",");
0244: while (tok.hasMoreTokens()) {
0245: tokens.add(tok.nextToken());
0246: }
0247: String[] ifaces = tokens.toArray(new String[0]);
0248: byte[] sup = new byte[2 + ifaces.length * 2];
0249: writeU2(sup, 0, ifaces.length);
0250:
0251: for (int i = 0; i < ifaces.length; i++) {
0252: int x = pc.addClass(ifaces[i]);
0253:
0254: writeU2(sup, 2 + i * 2, x);
0255: }
0256: pc.addAttribute(ATTR_INTERFACES, sup);
0257:
0258: patched = true;
0259: }
0260:
0261: if (!pc.isPublic()) {
0262: // will need patching
0263: pc.markPublic();
0264: patched = true;
0265: }
0266:
0267: if (methods != null) {
0268: for (String s : methods) {
0269: patched |= pc.markMemberPublic(s);
0270: }
0271: }
0272:
0273: if (rename != null) {
0274: Iterator<String> it = rename.iterator();
0275: while (it.hasNext()) {
0276: patched |= pc.renameMember(it.next(), it.next());
0277: }
0278: }
0279:
0280: if (patched) {
0281: byte[] patch = { 'n', 'b' // identification at the end of class file
0282: };
0283:
0284: pc.addAttribute("org.netbeans.enhanced", patch);
0285: } else {
0286: return null;
0287: }
0288:
0289: // otherwise do the patching
0290: return pc.getClassFile();
0291: }
0292:
0293: /** Checks if the class has previously been enhanced by the
0294: * change of superclass attribute and if so, changes the bytecode
0295: * to reflect the change.
0296: *
0297: * @param arr the bytecode
0298: * @param name the class name
0299: * @return the enhanced bytecode
0300: */
0301: public static byte[] patch(byte[] arr, String name) {
0302: if (!isPatched(arr))
0303: return arr;
0304:
0305: /*
0306: if (System.getProperty("test.class") != null) { // NOI18N
0307: // Running in XTest (ide-mode executor). Provide a little debug info.
0308: System.err.println("Patching: " + name); // NOI18N
0309: }
0310: */
0311:
0312: PatchByteCode pc = new PatchByteCode(arr, null);
0313: if (pc.super ClassNameAttr > 0) {
0314: // let's patch
0315: int classindex = pc.readU2(pc.super ClassNameAttr + 6);
0316:
0317: writeU2(pc.getClassFile(), pc.cpEnd + 4, classindex);
0318:
0319: if (pc.initAttr != -1) {
0320: // patch also CONSTANT_Methodref to superclass's <init>
0321: writeU2(pc.getClassFile(), pc.initAttr + 1, classindex);
0322: }
0323: }
0324:
0325: if (pc.memberClassAttr > 0) {
0326: // change the access rights of the class itself
0327: if (pc.readU4(pc.memberClassAttr + 2) != 2) {
0328: throw new IllegalArgumentException(
0329: "Size of a attribute " + ATTR_MEMBER
0330: + " should be 2"); // NOI18N
0331: }
0332:
0333: // alternate access rights
0334: int access = pc.readU2(pc.memberClassAttr + 6);
0335:
0336: /*int now = */pc.readU2(pc.cpEnd);
0337:
0338: writeU2(pc.getClassFile(), pc.cpEnd, access);
0339:
0340: }
0341:
0342: if (pc.memberNameIndex > 0 || pc.renameNameIndex > 0) {
0343: // change access rights of fields
0344: pc.applyMemberAccessAndNameChanges();
0345: }
0346:
0347: byte[] result = pc.getClassFile();
0348: if (pc.interfacesNameAttr > 0) {
0349: // let's patch interfaces if necessary
0350: int numberOfIfaces = pc.readU2(pc.interfacesNameAttr + 6);
0351: int currentIfaces = pc.readU2(pc.cpEnd + 6);
0352:
0353: byte[] insert = new byte[result.length + numberOfIfaces * 2];
0354: System.arraycopy(result, 0, insert, 0, pc.cpEnd + 6);
0355: System.arraycopy(result, pc.interfacesNameAttr + 8, insert,
0356: pc.cpEnd + 8, numberOfIfaces * 2);
0357: System.arraycopy(result, pc.cpEnd + 8, insert, pc.cpEnd + 8
0358: + numberOfIfaces * 2, result.length - pc.cpEnd - 8);
0359: writeU2(insert, pc.cpEnd + 6, numberOfIfaces
0360: + currentIfaces);
0361: result = insert;
0362: }
0363:
0364: return result;
0365: }
0366:
0367: /** Check if the byte code is patched.
0368: * @param arr the bytecode
0369: * @return true if patched
0370: */
0371: private static boolean isPatched(byte[] arr) {
0372: if (arr == null || arr.length < 2)
0373: return false;
0374:
0375: int base = arr.length - 2;
0376: if (arr[base + 1] != 'b')
0377: return false;
0378: if (arr[base + 0] != 'n')
0379: return false;
0380:
0381: //
0382: // ok, looks like enhanced byte code
0383: //
0384: return true;
0385: }
0386:
0387: /** Gets the current byte array of the actual class file.
0388: * @return bytes of the class file
0389: */
0390: private byte[] getClassFile() {
0391: return arr;
0392: }
0393:
0394: /** Creates new contant pool entry representing given class.
0395: * @param c name of the class
0396: * @return index of the entry
0397: */
0398: private int addClass(String s) {
0399: int x = addConstant(s);
0400:
0401: byte[] t = { 7, 0, 0 };
0402: writeU2(t, 1, x);
0403:
0404: return addPool(t);
0405: }
0406:
0407: /** Adds a new string constant to the constant pool.
0408: * @param s the string to add
0409: * @return index of the constant
0410: */
0411: private int addConstant(String s) {
0412: byte[] t;
0413:
0414: try {
0415: t = s.getBytes("utf-8"); // NOI18N
0416: } catch (java.io.UnsupportedEncodingException ex) {
0417: throw new IllegalStateException(
0418: "UTF-8 shall be always supported"); // NOI18N
0419: }
0420:
0421: byte[] add = new byte[t.length + 3];
0422: System.arraycopy(t, 0, add, 3, t.length);
0423: add[0] = 1; // UTF8 contant
0424: writeU2(add, 1, t.length);
0425:
0426: return addPool(add);
0427: }
0428:
0429: /** Adds this array of bytes as another entry into the constant pool */
0430: private int addPool(byte[] add) {
0431: byte[] res = new byte[arr.length + add.length];
0432:
0433: System.arraycopy(arr, 0, res, 0, cpEnd);
0434: // increments number of objects in contant pool
0435: int index = readU2(cpCount);
0436: writeU2(res, cpCount, index + 1);
0437:
0438: // adds the content
0439: System.arraycopy(add, 0, res, cpEnd, add.length);
0440:
0441: // and now add the rest of the original array
0442: System.arraycopy(arr, cpEnd, res, cpEnd + add.length,
0443: arr.length - cpEnd);
0444:
0445: arr = res;
0446:
0447: cpEnd += add.length;
0448: atCount += add.length;
0449: atEnd += add.length;
0450:
0451: // the index
0452: return index;
0453: }
0454:
0455: /** Checks whether the code is public.
0456: */
0457: private boolean isPublic() {
0458: int x = readU2(cpEnd);
0459:
0460: if ((x & 0x0001) != 0) {
0461: return true;
0462: } else {
0463: return false;
0464: }
0465: }
0466:
0467: /** Ensures that the class is public
0468: * @return true if really patched, false if not
0469: */
0470: private boolean markPublic() {
0471: if (isPublic()) {
0472: return false;
0473: }
0474:
0475: // make sure ATTR_MEMBER is in constant pool
0476: if (memberNameIndex == -1) {
0477: memberNameIndex = addConstant(ATTR_MEMBER);
0478: }
0479:
0480: int x = readU2(cpEnd) | 0x0001; // make it public
0481:
0482: byte[] sup = new byte[2];
0483: writeU2(sup, 0, x);
0484: addAttribute(ATTR_MEMBER, sup);
0485:
0486: return true;
0487: }
0488:
0489: /** Makes method of field public and non final.
0490: * @param name name of the method to make public
0491: * @return true if really changed, false if it already was public
0492: */
0493: private boolean markMemberPublic(String name) {
0494: int constantPoolIndex = nameIndexes.get(name)[0];
0495: int patchCount = 0;
0496: boolean modified = false;
0497:
0498: // make sure ATTR_MEMBER is in constant pool
0499: if (memberNameIndex == -1) {
0500: memberNameIndex = addConstant(ATTR_MEMBER);
0501: }
0502:
0503: int pos = cpEnd;
0504:
0505: pos += 6;
0506: // now add interfaces
0507: pos += 2 * readU2(pos);
0508: // to add also the integer with interfaces
0509: pos += 2;
0510:
0511: for (int fieldsAndMethods = 0; fieldsAndMethods < 2; fieldsAndMethods++) {
0512: // fields and then methods
0513: int fieldsOrMethods = readU2(pos);
0514: pos += 2;
0515:
0516: while (fieldsOrMethods-- > 0) {
0517: // check the name
0518: int nameIndex = readU2(pos + 2);
0519: if (nameIndex == constantPoolIndex) {
0520: // let's patch
0521: int access = readU2(pos);
0522: if ((access & 0x0001) == 0
0523: || (access & 0x0010) != 0) {
0524: // is not public or is final
0525: access = (access | 0x0001)
0526: & ~(0x0010 | 0x0002 | 0x0004);
0527:
0528: // increment the attributes count
0529: int cnt = readU2(pos + 6) + 1;
0530:
0531: //
0532: byte[] res = new byte[arr.length + 2 + 6];
0533:
0534: // copy the array before
0535: System.arraycopy(arr, 0, res, 0, pos + 6);
0536: // write the new count of attributes
0537: writeU2(res, pos + 6, cnt);
0538:
0539: // write the attribute itself
0540: writeU2(res, pos + 8, memberNameIndex); // name of attribute
0541: writeU4(res, pos + 10, 2); // length
0542: writeU2(res, pos + 14, access); // data - the "NetBeans" member modifier
0543:
0544: // copy the rest
0545: System.arraycopy(arr, pos + 8, res,
0546: pos + 8 + 6 + 2, arr.length - pos - 8);
0547:
0548: atEnd += 2 + 6;
0549: atCount += 2 + 6;
0550:
0551: arr = res;
0552:
0553: modified = true;
0554: }
0555:
0556: patchCount++;
0557: }
0558:
0559: pos += memberSize(pos, null);
0560: }
0561: }
0562:
0563: if (patchCount == 0) {
0564: throw new IllegalArgumentException("Member " + name
0565: + " not found!");
0566: }
0567:
0568: return modified;
0569: }
0570:
0571: /** Marks a field or method as one that should be renamed.
0572: * @param name name of the member
0573: * @param rename new name of the member
0574: * @return true if really changed, false if it already was renamed
0575: */
0576: private boolean renameMember(String name, String rename) {
0577: int constantPoolIndex = nameIndexes.get(name)[0];
0578: int newPoolIndex;
0579: {
0580: int[] arr = nameIndexes.get(rename);
0581: if (arr != null && arr[0] > 0) {
0582: newPoolIndex = arr[0];
0583: } else {
0584: newPoolIndex = addConstant(rename);
0585: nameIndexes.put(rename, new int[] { newPoolIndex });
0586: }
0587: }
0588: int patchCount = 0;
0589: boolean modified = false;
0590:
0591: // make sure ATTR_MEMBER is in constant pool
0592: if (renameNameIndex == -1) {
0593: renameNameIndex = addConstant(ATTR_NAME);
0594: }
0595:
0596: int pos = cpEnd;
0597:
0598: pos += 6;
0599: // now add interfaces
0600: pos += 2 * readU2(pos);
0601: // to add also the integer with interfaces
0602: pos += 2;
0603:
0604: for (int fieldsAndMethods = 0; fieldsAndMethods < 2; fieldsAndMethods++) {
0605: // fields and then methods
0606: int fieldsOrMethods = readU2(pos);
0607: pos += 2;
0608:
0609: while (fieldsOrMethods-- > 0) {
0610: // check the name
0611: int nameIndex = readU2(pos + 2);
0612: if (nameIndex == constantPoolIndex) {
0613: // check whether the rename attribute is not there yet
0614: int[] attributes = { -1, -1 };
0615:
0616: memberSize(pos, attributes);
0617: if (attributes[1] == -1) {
0618: // let's patch attribute is not there yet
0619:
0620: // increment the attributes count
0621: int cnt = readU2(pos + 6) + 1;
0622:
0623: //
0624: byte[] res = new byte[arr.length + 2 + 6];
0625:
0626: // copy the array before
0627: System.arraycopy(arr, 0, res, 0, pos + 6);
0628: // write the new count of attributes
0629: writeU2(res, pos + 6, cnt);
0630:
0631: // write the attribute itself
0632: writeU2(res, pos + 8, renameNameIndex); // name of attribute
0633: writeU4(res, pos + 10, 2); // length
0634: writeU2(res, pos + 14, newPoolIndex); // index to the new name
0635:
0636: // copy the rest
0637: System.arraycopy(arr, pos + 8, res,
0638: pos + 8 + 6 + 2, arr.length - pos - 8);
0639:
0640: atEnd += 2 + 6;
0641: atCount += 2 + 6;
0642:
0643: arr = res;
0644:
0645: modified = true;
0646: }
0647:
0648: patchCount++;
0649: }
0650:
0651: pos += memberSize(pos, null);
0652: }
0653: }
0654:
0655: if (patchCount == 0) {
0656: throw new IllegalArgumentException("Member " + name
0657: + " not found!");
0658: }
0659:
0660: return modified;
0661: }
0662:
0663: /** Checks all members of the class to find out whether they need patching
0664: * of access rights. If so, patches them.
0665: */
0666: private void applyMemberAccessAndNameChanges() {
0667: int[] result = new int[2];
0668:
0669: int pos = cpEnd;
0670:
0671: pos += 6;
0672: // now add interfaces
0673: pos += 2 * readU2(pos);
0674: // to add also the integer with interfaces
0675: pos += 2;
0676:
0677: for (int fieldsAndMethods = 0; fieldsAndMethods < 2; fieldsAndMethods++) {
0678: // fields and then methods
0679: int fieldsOrMethods = readU2(pos);
0680: pos += 2;
0681:
0682: while (fieldsOrMethods-- > 0) {
0683: result[0] = -1;
0684: result[1] = -1;
0685: int size = memberSize(pos, result);
0686: if (result[0] != -1) {
0687: // we will do patching
0688:
0689: if (readU4(result[0] + 2) != 2) {
0690: throw new IllegalArgumentException(
0691: "Size of a attribute " + ATTR_MEMBER
0692: + " should be 2"); // NOI18N
0693: }
0694:
0695: // alternate access rights
0696: int access = readU2(result[0] + 6);
0697: writeU2(arr, pos, access);
0698: }
0699:
0700: if (result[1] != -1) {
0701: // we will do patching
0702:
0703: if (readU4(result[1] + 2) != 2) {
0704: throw new IllegalArgumentException(
0705: "Size of a attribute " + ATTR_NAME
0706: + " should be 2"); // NOI18N
0707: }
0708:
0709: // alternate name of the member
0710: int newName = readU2(result[1] + 6);
0711: writeU2(arr, pos + 2, newName);
0712: }
0713:
0714: pos += size;
0715: }
0716: }
0717: }
0718:
0719: /** Adds an attribute to the class file.
0720: * @param name name of the attribute to add
0721: * @param b the bytes representing the attribute
0722: */
0723: private void addAttribute(String name, byte[] b) {
0724: int index = -1;
0725: if (ATTR_SUPERCLASS.equals(name) && super ClassNameIndex > 0) {
0726: index = super ClassNameIndex;
0727: }
0728:
0729: if (ATTR_MEMBER.equals(name) && memberNameIndex > 0) {
0730: index = memberNameIndex;
0731: }
0732:
0733: if (ATTR_INTERFACES.equals(name) && interfacesNameIndex > 0) {
0734: index = interfacesNameIndex;
0735: }
0736:
0737: if (index == -1) {
0738: // register new attribute
0739: index = addConstant(name);
0740: }
0741:
0742: byte[] res = new byte[arr.length + b.length + 6];
0743:
0744: System.arraycopy(arr, 0, res, 0, arr.length);
0745:
0746: writeU2(res, arr.length, index);
0747: writeU4(res, arr.length + 2, b.length);
0748:
0749: int begin = arr.length + 6;
0750: System.arraycopy(b, 0, res, begin, b.length);
0751:
0752: atEnd += b.length + 6;
0753:
0754: writeU2(res, atCount, readU2(atCount) + 1);
0755:
0756: arr = res;
0757: }
0758:
0759: /** Gets i-th element from the array.
0760: */
0761: private int get(int pos) {
0762: if (pos >= arr.length) {
0763: throw new ArrayIndexOutOfBoundsException("Size: "
0764: + arr.length + " index: " + pos);
0765: }
0766:
0767: int x = arr[pos];
0768: return x >= 0 ? x : 256 + x;
0769: }
0770:
0771: /** Scans the file to find out important possitions
0772: * @return size of the bytecode
0773: */
0774: private void scan() {
0775: if (get(0) != 0xCA && get(1) != 0xFE && get(2) != 0xBA
0776: && get(3) != 0xBE) {
0777: throw new IllegalStateException("Not a class file!"); // NOI18N
0778: }
0779:
0780: int pos = 10;
0781: // count of items in CP is here
0782: cpCount = 8;
0783:
0784: int cp = readU2(8);
0785: for (int[] i = { 1 }; i[0] < cp; i[0]++) {
0786: // i[0] can be incremented in constantPoolSize
0787: int len = constantPoolSize(pos, i);
0788: pos += len;
0789: }
0790:
0791: // list item in constant pool
0792: cpEnd = pos;
0793:
0794: pos += 6;
0795: // now add interfaces
0796: pos += 2 * readU2(pos);
0797: // to add also the integer with interfaces
0798: pos += 2;
0799:
0800: // fields
0801: int fields = readU2(pos);
0802: pos += 2;
0803: while (fields-- > 0) {
0804: pos += memberSize(pos, null);
0805: }
0806:
0807: int methods = readU2(pos);
0808: pos += 2;
0809: while (methods-- > 0) {
0810: pos += memberSize(pos, null);
0811: }
0812:
0813: // count of items in Attributes is here
0814:
0815: int[] memberAccess = { -1, -1 };
0816:
0817: atCount = pos;
0818: int attrs = readU2(pos);
0819: pos += 2;
0820: while (attrs-- > 0) {
0821: pos += attributeSize(pos, memberAccess);
0822: }
0823:
0824: if (memberAccess[0] != -1) {
0825: // we need to update the name of class
0826: memberClassAttr = memberAccess[0];
0827: }
0828:
0829: // end of attributes
0830: atEnd = pos;
0831: }
0832:
0833: private int readU2(int pos) {
0834: int b1 = get(pos);
0835: int b2 = get(pos + 1);
0836:
0837: return b1 * 256 + b2;
0838: }
0839:
0840: private int readU4(int pos) {
0841: return readU2(pos) * 256 * 256 + readU2(pos + 2);
0842: }
0843:
0844: private static void writeU2(byte[] arr, int pos, int value) {
0845: int v1 = (value & 0xff00) >> 8;
0846: int v2 = value & 0xff;
0847:
0848: if (v1 < 0)
0849: v1 += 256;
0850: if (v2 < 0)
0851: v2 += 256;
0852:
0853: arr[pos] = (byte) v1;
0854: arr[pos + 1] = (byte) v2;
0855: }
0856:
0857: private static void writeU4(byte[] arr, int pos, int value) {
0858: writeU2(arr, pos, (value & 0xff00) >> 16);
0859: writeU2(arr, pos + 2, value & 0x00ff);
0860: }
0861:
0862: /** @param pos position to read from
0863: * @param cnt[0] index in the pool that we are now reading
0864: */
0865: private int constantPoolSize(int pos, int[] cnt) {
0866: switch (get(pos)) {
0867: case 7: // CONSTANT_Class
0868: case 8: // CONSTANT_String
0869: return 3;
0870:
0871: case 12: // CONSTANT_NameAndType
0872: // check for <init> and ()V invocation
0873: int nameIndex = readU2(pos + 1);
0874: if (nameIndex == initIndex) {
0875: int descriptorIndex = readU2(pos + 3);
0876: if (descriptorIndex == initIndexType) {
0877: if (initNameTypeIndex > 0
0878: && initNameTypeIndex != cnt[0]) {
0879: throw new IllegalArgumentException(
0880: "Second initialization of name type index"); // NOI18N
0881: }
0882: initNameTypeIndex = cnt[0];
0883: }
0884: }
0885: return 5;
0886:
0887: case 10: // CONSTANT_Methodref
0888: // special check for <init> invocation
0889: int classname = readU2(pos + 1);
0890: int nameAndType = readU2(pos + 3);
0891: if (nameAndType == initNameTypeIndex) {
0892: // found call to <init>
0893: int super class = readU2(cpEnd + 4);
0894:
0895: if (super class == classname) {
0896: // it is call to our superclass
0897: if (initAttr > 0 && initAttr != pos) {
0898: throw new IllegalStateException(
0899: "Second initialization of position of <init> invocation"); // NOI18N
0900: }
0901: initAttr = pos;
0902: }
0903: }
0904: return 5;
0905:
0906: case 9: // CONSTANT_Fieldref
0907: case 11: // CONSTANT_InterfaceMethodref
0908: case 3: // CONSTANT_Integer
0909: case 4: // CONSTANT_Float
0910: return 5;
0911:
0912: case 5: // CONSTANT_Long
0913: case 6: // CONSTANT_Double
0914: // after long and double the next entry in CP is unusable
0915: cnt[0]++;
0916: return 9;
0917: case 1: // CONSTANT_Utf8
0918: int len = readU2(pos + 1);
0919:
0920: if (compareUtfEntry(BYTE_INIT, pos)) {
0921: if (initIndex > 0 && initIndex != cnt[0]) {
0922: throw new IllegalArgumentException(
0923: "Second initialization of " + ATTR_INIT); // NOI18N
0924: }
0925: initIndex = cnt[0];
0926: }
0927:
0928: if (compareUtfEntry(BYTE_INIT_TYPE, pos)) {
0929: if (initIndexType > 0 && initIndexType != cnt[0]) {
0930: throw new IllegalArgumentException(
0931: "Second initialization of "
0932: + ATTR_INIT_TYPE); // NOI18N
0933: }
0934: initIndexType = cnt[0];
0935: }
0936:
0937: if (compareUtfEntry(BYTE_SUPERCLASS, pos)) {
0938: // we have found the attribute
0939: if (super ClassNameIndex > 0
0940: && super ClassNameIndex != cnt[0]) {
0941: throw new IllegalStateException(ATTR_SUPERCLASS
0942: + " registered for the second time!"); // NOI18N
0943: }
0944:
0945: super ClassNameIndex = cnt[0];
0946: }
0947:
0948: if (compareUtfEntry(BYTE_INTERFACES, pos)) {
0949: // we have found the attribute
0950: if (interfacesNameIndex > 0
0951: && interfacesNameIndex != cnt[0]) {
0952: throw new IllegalStateException(ATTR_INTERFACES
0953: + " registered for the second time!"); // NOI18N
0954: }
0955:
0956: interfacesNameIndex = cnt[0];
0957: }
0958:
0959: if (compareUtfEntry(BYTE_MEMBER, pos)) {
0960: // we have found the attribute
0961: if (memberNameIndex > 0 && memberNameIndex != cnt[0]) {
0962: throw new IllegalStateException(ATTR_MEMBER
0963: + " registered for the second time!"); // NOI18N
0964: }
0965:
0966: memberNameIndex = cnt[0];
0967: }
0968: if (compareUtfEntry(BYTE_NAME, pos)) {
0969: // we have found the attribute
0970: if (renameNameIndex > 0 && renameNameIndex != cnt[0]) {
0971: throw new IllegalStateException(ATTR_NAME
0972: + " registered for the second time!"); // NOI18N
0973: }
0974:
0975: renameNameIndex = cnt[0];
0976: }
0977:
0978: if (nameIndexes != null) {
0979: // check the name in the table
0980: String s;
0981: try {
0982: s = new String(arr, pos + 3, len, "utf-8"); // NOI18N
0983: } catch (UnsupportedEncodingException ex) {
0984: throw new IllegalStateException(
0985: "utf-8 is always supported"); // NOI18N
0986: }
0987:
0988: int[] index = nameIndexes.get(s);
0989: if (index != null) {
0990: index[0] = cnt[0];
0991: }
0992: }
0993:
0994: // ok, exit
0995: return len + 3;
0996: default:
0997: throw new IllegalStateException("Unknown pool type: "
0998: + get(pos)); // NOI18N
0999: }
1000: }
1001:
1002: private int memberSize(int pos,
1003: int[] containsPatchAttributeAndRename) {
1004: int s = 8;
1005: /*int name = */readU2(pos + 2);
1006:
1007: int cnt = readU2(pos + 6);
1008:
1009: while (cnt-- > 0) {
1010: s += attributeSize(pos + s, containsPatchAttributeAndRename);
1011: }
1012: return s;
1013: }
1014:
1015: /** Into the containsPatchAttribute (if not null) it adds the
1016: * index of structure of attribute ATTR_MEMBER.
1017: */
1018: private int attributeSize(int pos,
1019: int[] containsPatchAttributeAndRename) {
1020: // index to the name attr
1021: int name = readU2(pos);
1022:
1023: if (name == super ClassNameIndex) {
1024: if (super ClassNameAttr > 0 && super ClassNameAttr != pos) {
1025: throw new IllegalStateException(
1026: "Two attributes with name " + ATTR_SUPERCLASS); // NOI18N
1027: }
1028:
1029: // we found the attribute
1030: super ClassNameAttr = pos;
1031: }
1032:
1033: if (name == interfacesNameIndex) {
1034: if (interfacesNameAttr > 0 && interfacesNameAttr != pos) {
1035: throw new IllegalStateException(
1036: "Two attributes with name " + ATTR_INTERFACES); // NOI18N
1037: }
1038:
1039: // we found the attribute
1040: interfacesNameAttr = pos;
1041: }
1042:
1043: if (name == memberNameIndex
1044: && containsPatchAttributeAndRename != null) {
1045: if (containsPatchAttributeAndRename[0] != -1) {
1046: throw new IllegalStateException("Second attribute "
1047: + ATTR_MEMBER); // NOI18N
1048: }
1049: containsPatchAttributeAndRename[0] = pos;
1050: }
1051:
1052: if (name == renameNameIndex
1053: && containsPatchAttributeAndRename != null) {
1054: if (containsPatchAttributeAndRename[1] != -1) {
1055: throw new IllegalStateException("Second attribute "
1056: + ATTR_NAME); // NOI18N
1057: }
1058: containsPatchAttributeAndRename[1] = pos;
1059: }
1060:
1061: int len = readU4(pos + 2);
1062: return len + 6;
1063: }
1064:
1065: /** Compares arrays.
1066: */
1067: private boolean compareUtfEntry(byte[] pattern, int pos) {
1068: int len = readU2(pos + 1);
1069:
1070: if (pattern.length != len) {
1071: return false;
1072: }
1073:
1074: int base = pos + 3;
1075: // we are searching for an attribute with given name
1076: for (int i = 0; i < len; i++) {
1077: if (pattern[i] != arr[base + i]) {
1078: // regular exit
1079: return false;
1080: }
1081: }
1082:
1083: return true;
1084: }
1085: }
|