001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.bytecode;
031:
032: import com.caucho.log.Log;
033: import com.caucho.util.CharBuffer;
034: import com.caucho.util.L10N;
035:
036: import java.io.IOException;
037: import java.io.InputStream;
038: import java.util.ArrayList;
039: import java.util.logging.Logger;
040:
041: /**
042: * Interface to the bytecode parser.
043: */
044: public class ByteCodeParser {
045: private static final Logger log = Log.open(ByteCode.class);
046: private static final L10N L = new L10N(ByteCode.class);
047:
048: static final int CP_CLASS = 7;
049: static final int CP_FIELD_REF = 9;
050: static final int CP_METHOD_REF = 10;
051: static final int CP_INTERFACE_METHOD_REF = 11;
052: static final int CP_STRING = 8;
053: static final int CP_INTEGER = 3;
054: static final int CP_FLOAT = 4;
055: static final int CP_LONG = 5;
056: static final int CP_DOUBLE = 6;
057: static final int CP_NAME_AND_TYPE = 12;
058: static final int CP_UTF8 = 1;
059:
060: private JavaClassLoader _loader;
061: private InputStream _is;
062: private JavaClass _class;
063: private ConstantPool _cp;
064:
065: /**
066: * Sets the JClassLoader
067: */
068: public void setClassLoader(JavaClassLoader loader) {
069: _loader = loader;
070: }
071:
072: /**
073: * Sets the class.
074: */
075: public void setJavaClass(JavaClass javaClass) {
076: _class = javaClass;
077: }
078:
079: /**
080: * Parses the .class file.
081: */
082: public JavaClass parse(InputStream is) throws IOException {
083: _is = is;
084:
085: if (_loader == null)
086: _loader = new JavaClassLoader();
087:
088: if (_class == null)
089: _class = new JavaClass(_loader);
090:
091: _cp = _class.getConstantPool();
092:
093: parseClass();
094:
095: return _class;
096: }
097:
098: /**
099: * Returns the constant pool.
100: */
101: public ConstantPool getConstantPool() {
102: return _cp;
103: }
104:
105: /**
106: * Returns a UTF8 String from the constant pool.
107: */
108: public String getUTF8(int index) {
109: return getConstantPool().getUtf8AsString(index);
110: }
111:
112: /**
113: * Parses the ClassFile construct
114: */
115: private void parseClass() throws IOException {
116: int magic = readInt();
117:
118: if (magic != JavaClass.MAGIC)
119: throw error(L.l("bad magic number in class file"));
120:
121: int minor = readShort();
122: int major = readShort();
123:
124: _class.setMajor(major);
125: _class.setMinor(minor);
126:
127: parseConstantPool();
128:
129: int accessFlags = readShort();
130: _class.setAccessFlags(accessFlags);
131:
132: int this ClassIndex = readShort();
133: _class.setThisClass(_cp.getClass(this ClassIndex).getName());
134:
135: int super ClassIndex = readShort();
136: if (super ClassIndex > 0)
137: _class.setSuperClass(_cp.getClass(super ClassIndex)
138: .getName());
139:
140: int interfaceCount = readShort();
141: for (int i = 0; i < interfaceCount; i++) {
142: int classIndex = readShort();
143:
144: _class.addInterface(_cp.getClass(classIndex).getName());
145: }
146:
147: int fieldCount = readShort();
148: for (int i = 0; i < fieldCount; i++) {
149: parseField();
150: }
151:
152: int methodCount = readShort();
153: for (int i = 0; i < methodCount; i++)
154: parseMethod();
155:
156: int attrCount = readShort();
157: for (int i = 0; i < attrCount; i++) {
158: Attribute attr = parseAttribute();
159:
160: _class.addAttribute(attr);
161: }
162: }
163:
164: /**
165: * Parses the constant pool.
166: */
167: public void parseConstantPool() throws IOException {
168: int count = readShort();
169:
170: for (int i = 1; i < count; i++) {
171: ConstantPoolEntry entry = parseConstantPoolEntry(i);
172:
173: _cp.addConstant(entry);
174:
175: if (entry instanceof DoubleConstant
176: || entry instanceof LongConstant) {
177: i++;
178: _cp.addConstant(null);
179: }
180: }
181: }
182:
183: /**
184: * Parses a constant pool entry.
185: */
186: private ConstantPoolEntry parseConstantPoolEntry(int index)
187: throws IOException {
188: int tag = read();
189:
190: switch (tag) {
191: case CP_CLASS:
192: return parseClassConstant(index);
193:
194: case CP_FIELD_REF:
195: return parseFieldRefConstant(index);
196:
197: case CP_METHOD_REF:
198: return parseMethodRefConstant(index);
199:
200: case CP_INTERFACE_METHOD_REF:
201: return parseInterfaceMethodRefConstant(index);
202:
203: case CP_STRING:
204: return parseStringConstant(index);
205:
206: case CP_INTEGER:
207: return parseIntegerConstant(index);
208:
209: case CP_FLOAT:
210: return parseFloatConstant(index);
211:
212: case CP_LONG:
213: return parseLongConstant(index);
214:
215: case CP_DOUBLE:
216: return parseDoubleConstant(index);
217:
218: case CP_NAME_AND_TYPE:
219: return parseNameAndTypeConstant(index);
220:
221: case CP_UTF8:
222: return parseUtf8Constant(index);
223:
224: default:
225: throw error(L.l("'{0}' is an unknown constant pool type.",
226: tag));
227: }
228: }
229:
230: /**
231: * Parses a class constant pool entry.
232: */
233: private ClassConstant parseClassConstant(int index)
234: throws IOException {
235: int nameIndex = readShort();
236:
237: return new ClassConstant(_class.getConstantPool(), index,
238: nameIndex);
239: }
240:
241: /**
242: * Parses a field ref constant pool entry.
243: */
244: private FieldRefConstant parseFieldRefConstant(int index)
245: throws IOException {
246: int classIndex = readShort();
247: int nameAndTypeIndex = readShort();
248:
249: return new FieldRefConstant(_class.getConstantPool(), index,
250: classIndex, nameAndTypeIndex);
251: }
252:
253: /**
254: * Parses a method ref constant pool entry.
255: */
256: private MethodRefConstant parseMethodRefConstant(int index)
257: throws IOException {
258: int classIndex = readShort();
259: int nameAndTypeIndex = readShort();
260:
261: return new MethodRefConstant(_class.getConstantPool(), index,
262: classIndex, nameAndTypeIndex);
263: }
264:
265: /**
266: * Parses an interface method ref constant pool entry.
267: */
268: private InterfaceMethodRefConstant parseInterfaceMethodRefConstant(
269: int index) throws IOException {
270: int classIndex = readShort();
271: int nameAndTypeIndex = readShort();
272:
273: return new InterfaceMethodRefConstant(_class.getConstantPool(),
274: index, classIndex, nameAndTypeIndex);
275: }
276:
277: /**
278: * Parses a string constant pool entry.
279: */
280: private StringConstant parseStringConstant(int index)
281: throws IOException {
282: int stringIndex = readShort();
283:
284: return new StringConstant(_class.getConstantPool(), index,
285: stringIndex);
286: }
287:
288: /**
289: * Parses an integer constant pool entry.
290: */
291: private IntegerConstant parseIntegerConstant(int index)
292: throws IOException {
293: int value = readInt();
294:
295: return new IntegerConstant(_class.getConstantPool(), index,
296: value);
297: }
298:
299: /**
300: * Parses a float constant pool entry.
301: */
302: private FloatConstant parseFloatConstant(int index)
303: throws IOException {
304: int bits = readInt();
305:
306: float value = Float.intBitsToFloat(bits);
307:
308: return new FloatConstant(_class.getConstantPool(), index, value);
309: }
310:
311: /**
312: * Parses a long constant pool entry.
313: */
314: private LongConstant parseLongConstant(int index)
315: throws IOException {
316: long value = readLong();
317:
318: return new LongConstant(_class.getConstantPool(), index, value);
319: }
320:
321: /**
322: * Parses a double constant pool entry.
323: */
324: private DoubleConstant parseDoubleConstant(int index)
325: throws IOException {
326: long bits = readLong();
327:
328: double value = Double.longBitsToDouble(bits);
329:
330: return new DoubleConstant(_class.getConstantPool(), index,
331: value);
332: }
333:
334: /**
335: * Parses a name and type pool entry.
336: */
337: private NameAndTypeConstant parseNameAndTypeConstant(int index)
338: throws IOException {
339: int nameIndex = readShort();
340: int descriptorIndex = readShort();
341:
342: return new NameAndTypeConstant(_class.getConstantPool(), index,
343: nameIndex, descriptorIndex);
344: }
345:
346: /**
347: * Parses a utf-8 constant pool entry.
348: */
349: private Utf8Constant parseUtf8Constant(int index)
350: throws IOException {
351: int length = readShort();
352:
353: CharBuffer cb = CharBuffer.allocate();
354:
355: for (int i = 0; i < length; i++) {
356: int ch = read();
357:
358: if (ch < 0x80) {
359: cb.append((char) ch);
360: } else if ((ch & 0xe0) == 0xc0) {
361: int ch2 = read();
362: i++;
363:
364: cb.append((char) (((ch & 0x1f) << 6) + (ch2 & 0x3f)));
365: } else {
366: int ch2 = read();
367: int ch3 = read();
368: i += 2;
369:
370: cb.append((char) (((ch & 0xf) << 12)
371: + ((ch2 & 0x3f) << 6) + ((ch3 & 0x3f))));
372: }
373: }
374:
375: return new Utf8Constant(_class.getConstantPool(), index, cb
376: .close());
377: }
378:
379: /**
380: * Parses a field entry.
381: */
382: private void parseField() throws IOException {
383: int accessFlags = readShort();
384: int nameIndex = readShort();
385: int descriptorIndex = readShort();
386:
387: JavaField field = new JavaField();
388: field.setJavaClass(_class);
389: field.setName(_cp.getUtf8(nameIndex).getValue());
390: field.setDescriptor(_cp.getUtf8(descriptorIndex).getValue());
391: field.setAccessFlags(accessFlags);
392:
393: int attributesCount = readShort();
394:
395: for (int i = 0; i < attributesCount; i++) {
396: Attribute attr = parseAttribute();
397:
398: field.addAttribute(attr);
399: }
400:
401: _class.addField(field);
402: }
403:
404: /**
405: * Parses a method entry.
406: */
407: private void parseMethod() throws IOException {
408: int accessFlags = readShort();
409: int nameIndex = readShort();
410: int descriptorIndex = readShort();
411:
412: JavaMethod method = new JavaMethod(_loader);
413: method.setJavaClass(_class);
414: method.setName(_cp.getUtf8(nameIndex).getValue());
415: method.setDescriptor(_cp.getUtf8(descriptorIndex).getValue());
416: method.setAccessFlags(accessFlags);
417:
418: int attributesCount = readShort();
419:
420: for (int i = 0; i < attributesCount; i++) {
421: Attribute attr = parseAttribute();
422:
423: method.addAttribute(attr);
424:
425: if (attr instanceof ExceptionsAttribute) {
426: ExceptionsAttribute exn = (ExceptionsAttribute) attr;
427:
428: ArrayList<String> exnNames = exn.getExceptionList();
429:
430: if (exnNames.size() > 0) {
431: JClass[] exnClasses = new JClass[exnNames.size()];
432:
433: for (int j = 0; j < exnNames.size(); j++) {
434: String exnName = exnNames.get(j).replace('/',
435: '.');
436:
437: exnClasses[j] = _loader.forName(exnName);
438: }
439:
440: method.setExceptionTypes(exnClasses);
441: }
442: }
443: }
444:
445: _class.addMethod(method);
446: }
447:
448: /**
449: * Parses an attribute.
450: */
451: Attribute parseAttribute() throws IOException {
452: int nameIndex = readShort();
453:
454: String name = _cp.getUtf8(nameIndex).getValue();
455:
456: if (name.equals("Code")) {
457: CodeAttribute code = new CodeAttribute(name);
458: code.read(this );
459: return code;
460: } else if (name.equals("Exceptions")) {
461: ExceptionsAttribute code = new ExceptionsAttribute(name);
462: code.read(this );
463: return code;
464: } else if (name.equals("Signature")) {
465: SignatureAttribute code = new SignatureAttribute();
466: code.read(this );
467: return code;
468: }
469:
470: OpaqueAttribute attr = new OpaqueAttribute(name);
471:
472: int length = readInt();
473:
474: byte[] bytes = new byte[length];
475:
476: read(bytes, 0, bytes.length);
477:
478: attr.setValue(bytes);
479:
480: return attr;
481: }
482:
483: /**
484: * Parses a 64-bit int.
485: */
486: long readLong() throws IOException {
487: return (((long) _is.read() << 56) | ((long) _is.read() << 48)
488: | ((long) _is.read() << 40) | ((long) _is.read() << 32)
489: | ((long) _is.read() << 24) | ((long) _is.read() << 16)
490: | ((long) _is.read() << 8) | ((long) _is.read()));
491: }
492:
493: /**
494: * Parses a 32-bit int.
495: */
496: int readInt() throws IOException {
497: return ((_is.read() << 24) | (_is.read() << 16)
498: | (_is.read() << 8) | (_is.read()));
499: }
500:
501: /**
502: * Parses a 16-bit int.
503: */
504: int readShort() throws IOException {
505: int c1 = _is.read();
506: int c2 = _is.read();
507:
508: return ((c1 << 8) | c2);
509: }
510:
511: /**
512: * Parses a byte
513: */
514: int read() throws IOException {
515: return _is.read();
516: }
517:
518: /**
519: * Reads a chunk
520: */
521: int read(byte[] buffer, int offset, int length) throws IOException {
522: int readLength = 0;
523:
524: while (length > 0) {
525: int sublen = _is.read(buffer, offset, length);
526:
527: if (sublen < 0)
528: return readLength == 0 ? -1 : readLength;
529:
530: offset += sublen;
531: length -= sublen;
532: readLength += sublen;
533: }
534:
535: return readLength;
536: }
537:
538: /**
539: * Returns an error message.
540: */
541: private IOException error(String message) {
542: return new IOException(message);
543: }
544: }
|