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.ByteBuffer;
034: import com.caucho.util.IntArray;
035: import com.caucho.util.L10N;
036:
037: import java.util.ArrayList;
038: import java.util.logging.Level;
039: import java.util.logging.Logger;
040:
041: /**
042: * Visitor for travelling the code.
043: */
044: public class CodeEnhancer extends CodeVisitor {
045: static private final Logger log = Log.open(CodeEnhancer.class);
046: static private final L10N L = new L10N(CodeEnhancer.class);
047:
048: private ByteBuffer _code;
049:
050: private ArrayList<Jump> _jumps;
051: private ArrayList<Switch> _switches;
052: private boolean _changeLength;
053:
054: // already visited targets
055: private IntArray _pendingTargets;
056: private IntArray _completedTargets;
057:
058: public CodeEnhancer() {
059: }
060:
061: public CodeEnhancer(JavaClass javaClass, CodeAttribute code) {
062: init(javaClass, code);
063: }
064:
065: public void init(JavaClass javaClass, CodeAttribute codeAttr) {
066: super .init(javaClass, codeAttr);
067:
068: _code = new ByteBuffer();
069:
070: byte[] codeBuffer = codeAttr.getCode();
071:
072: _code.add(codeBuffer, 0, codeBuffer.length);
073:
074: _changeLength = false;
075: }
076:
077: /**
078: * Analyzes the code for a method
079: */
080: public void analyze(Analyzer analyzer, boolean allowFlow)
081: throws Exception {
082: _pendingTargets = new IntArray();
083: _completedTargets = new IntArray();
084:
085: analyzeImpl(analyzer, allowFlow, _pendingTargets,
086: _completedTargets);
087: }
088:
089: /**
090: * Returns the code buffer.
091: */
092: public byte[] getCode() {
093: return _code.getBuffer();
094: }
095:
096: /**
097: * Returns the length.
098: */
099: public int getLength() {
100: return _code.getLength();
101: }
102:
103: /**
104: * Adds a byte to the code.
105: */
106: public void addByte(int offset, int value) {
107: insertCode(offset, 1);
108:
109: _code.set(offset, value);
110: }
111:
112: /**
113: * Adds a byte to the code.
114: */
115: public void setByte(int offset, int value) {
116: _code.set(offset, value);
117: }
118:
119: /**
120: * Adds a short to the code.
121: */
122: public void addShort(int offset, int value) {
123: insertCode(offset, 2);
124:
125: _code.set(offset + 0, value >> 8);
126: _code.set(offset + 1, value);
127: }
128:
129: /**
130: * Adds a byte to the code.
131: */
132: public void add(int offset, byte[] buffer, int bufOffset, int length) {
133: insertCode(offset, length);
134:
135: _code.set(offset, buffer, bufOffset, length);
136: }
137:
138: /**
139: * Removes a range from the code.
140: */
141: public void remove(int offset, int count) {
142: removeCode(offset, count);
143: }
144:
145: /**
146: * Adds a byte to the code.
147: */
148: public void addNulls(int offset, int count) {
149: insertCode(offset, count);
150: }
151:
152: /**
153: * Updates indices when adding a chunk of code. The operation at
154: * the given offset moves, e.g. adding 6 bytes to the beginning of
155: * the program moves the initial byte down by 6 and therefore needs
156: * to update the links as well.
157: *
158: * Therefore, enhancers which expand an opcode from 2 bytes to 3 bytes
159: * must insert the new bytes after the initial opcode.
160: */
161: protected void insertCode(int offset, int count) {
162: if (_jumps == null)
163: analyzeJumps();
164:
165: // XXX: revisits the new code
166: if (offset <= _offset) {
167: _offset += count;
168: }
169:
170: for (int i = 0; i < _jumps.size(); i++) {
171: Jump jump = _jumps.get(i);
172:
173: jump.insert(this , offset, count);
174: }
175:
176: ArrayList<CodeAttribute.ExceptionItem> exns = getExceptions();
177:
178: for (int i = 0; i < exns.size(); i++) {
179: CodeAttribute.ExceptionItem exn = exns.get(i);
180:
181: if (offset <= exn.getStart())
182: exn.setStart(exn.getStart() + count);
183:
184: if (offset <= exn.getEnd())
185: exn.setEnd(exn.getEnd() + count);
186:
187: if (offset <= exn.getHandler())
188: exn.setHandler(exn.getHandler() + count);
189: }
190:
191: if (_pendingTargets != null) {
192: for (int i = _pendingTargets.size() - 1; i >= 0; i--) {
193: int target = _pendingTargets.get(i);
194:
195: if (offset <= target)
196: _pendingTargets.set(i, target + count);
197: }
198:
199: for (int i = _completedTargets.size() - 1; i >= 0; i--) {
200: int target = _completedTargets.get(i);
201:
202: if (offset <= target)
203: _completedTargets.set(i, target + count);
204: }
205: }
206:
207: for (int i = 0; i < _switches.size(); i++) {
208: Branch branch = _switches.get(i);
209:
210: branch.insert(this , offset, count);
211: }
212:
213: for (int i = 0; i < count; i++)
214: _code.add(offset, 0);
215:
216: for (int i = 0; i < _switches.size(); i++) {
217: Switch branch = _switches.get(i);
218:
219: branch.insertPad(this , offset, count);
220: }
221: }
222:
223: protected void removeCode(int offset, int count) {
224: if (_jumps == null)
225: analyzeJumps();
226:
227: if (offset + count < _offset)
228: _offset -= count;
229: else if (offset <= _offset)
230: _offset = offset;
231:
232: for (int i = 0; i < _jumps.size(); i++) {
233: Branch jump = _jumps.get(i);
234:
235: jump.remove(this , offset, count);
236: }
237:
238: ArrayList<CodeAttribute.ExceptionItem> exns = getExceptions();
239:
240: for (int i = 0; i < exns.size(); i++) {
241: CodeAttribute.ExceptionItem exn = exns.get(i);
242:
243: exn.setStart(remove(exn.getStart(), offset, count));
244: exn.setEnd(remove(exn.getEnd(), offset, count));
245: exn.setHandler(remove(exn.getHandler(), offset, count));
246: }
247:
248: if (_pendingTargets != null) {
249: for (int i = _pendingTargets.size() - 1; i >= 0; i--) {
250: int target = _pendingTargets.get(i);
251:
252: _pendingTargets.set(i, remove(target, offset, count));
253: }
254:
255: for (int i = _completedTargets.size() - 1; i >= 0; i--) {
256: int target = _completedTargets.get(i);
257:
258: _completedTargets.set(i, remove(target, offset, count));
259: }
260: }
261:
262: for (int i = 0; i < _switches.size(); i++) {
263: Branch branch = _switches.get(i);
264:
265: branch.remove(this , offset, count);
266: }
267:
268: _code.remove(offset, count);
269:
270: for (int i = 0; i < _switches.size(); i++) {
271: Switch branch = _switches.get(i);
272:
273: branch.removePad(this , offset, count);
274: }
275: }
276:
277: protected void analyzeJumps() {
278: _jumps = new ArrayList<Jump>();
279: _switches = new ArrayList<Switch>();
280:
281: _changeLength = true;
282:
283: JumpAnalyzer analyzer = new JumpAnalyzer();
284:
285: CodeVisitor visitor = new CodeVisitor(getJavaClass(),
286: getCodeAttribute());
287:
288: try {
289: visitor.analyze(analyzer);
290: } catch (Exception e) {
291: log.log(Level.WARNING, e.toString(), e);
292: }
293: }
294:
295: /**
296: * Updates the code.
297: */
298: public void update() {
299: byte[] code = new byte[_code.size()];
300:
301: System.arraycopy(_code.getBuffer(), 0, code, 0, _code.size());
302:
303: _codeAttr.setCode(code);
304:
305: if (_changeLength) {
306: // XXX: really need more sophisticated solution
307: ArrayList<Attribute> attrList = getCodeAttribute()
308: .getAttributes();
309: for (int i = attrList.size() - 1; i >= 0; i--) {
310: Attribute attr = attrList.get(i);
311:
312: if (attr.getName().equals("LineNumberTable"))
313: attrList.remove(i);
314: }
315: }
316: }
317:
318: private int remove(int pc, int offset, int count) {
319: if (pc < offset)
320: return pc;
321: else if (pc < offset + count)
322: return offset;
323: else
324: return pc - count;
325: }
326:
327: abstract static class Branch {
328: abstract void insert(CodeEnhancer enhancer, int offset,
329: int count);
330:
331: abstract void remove(CodeEnhancer enhancer, int offset,
332: int count);
333: }
334:
335: static class Jump extends Branch {
336: private int _src;
337: private int _delta;
338:
339: Jump(int src, int delta) {
340: _src = src;
341: _delta = delta;
342: }
343:
344: void insert(CodeEnhancer enhancer, int offset, int count) {
345: // offset is before the jump
346: if (offset <= _src && offset <= _src + _delta) {
347: _src += count;
348: }
349: // offset is inside a forward jump
350: else if (_src < offset && offset < _src + _delta) {
351: _delta += count;
352: enhancer.setShort(_src + 1, _delta);
353: }
354: // offset is inside a backward jump
355: else if (_src + _delta <= offset && offset <= _src) {
356: _delta -= count;
357: enhancer.setShort(_src + 1, _delta);
358: _src += count;
359: }
360: }
361:
362: void remove(CodeEnhancer enhancer, int offset, int count) {
363: // offset is before the jump
364: if (offset <= _src && offset <= _src + _delta) {
365: _src -= count;
366: }
367: // offset is inside a forward jump
368: else if (_src < offset && offset < _src + _delta) {
369: _delta -= count;
370: enhancer.setShort(_src + 1, _delta);
371: }
372: // offset is inside a backward jump
373: else if (_src + _delta <= offset && offset <= _src) {
374: _delta += count;
375: enhancer.setShort(_src + 1, _delta);
376: _src -= count;
377: }
378: }
379: }
380:
381: static class Switch extends Branch {
382: private int _oldSrc;
383: private int _src;
384: private int[] _offsets;
385:
386: Switch(int src) {
387: _src = src;
388: _oldSrc = src;
389: }
390:
391: protected void setOffsets(int[] offsets) {
392: _offsets = offsets;
393: }
394:
395: void insert(CodeEnhancer enhancer, int offset, int count) {
396: for (int i = 0; i < _offsets.length; i++) {
397: int delta = enhancer.getInt(_offsets[i]);
398:
399: if (offset <= _src && _src + delta <= offset)
400: enhancer.setInt(_offsets[i], delta - count);
401: else if (_src < offset && offset < _src + delta)
402: enhancer.setInt(_offsets[i], delta + count);
403:
404: if (offset <= _src + 1)
405: _offsets[i] += count;
406: }
407:
408: if (offset < _src)
409: _src += count;
410: }
411:
412: void remove(CodeEnhancer enhancer, int offset, int count) {
413: for (int i = 0; i < _offsets.length; i++) {
414: int delta = enhancer.getInt(_offsets[i]);
415:
416: if (offset <= _src && _src + delta <= offset)
417: enhancer.setInt(_offsets[i], delta + count);
418: else if (_src < offset && offset < _src + delta)
419: enhancer.setInt(_offsets[i], delta - count);
420:
421: if (offset <= _src + 1)
422: _offsets[i] -= count;
423: }
424:
425: if (offset < _src)
426: _src -= count;
427: }
428:
429: void insertPad(CodeEnhancer enhancer, int offset, int count) {
430: // offset is before the jump
431: if (_oldSrc != _src) {
432: int oldPad = (4 - (_oldSrc + 1) % 4) % 4;
433: int newPad = (4 - (_src + 1) % 4) % 4;
434:
435: _oldSrc = _src;
436:
437: if (newPad < oldPad)
438: enhancer.remove(_src + 1, oldPad - newPad);
439: else if (oldPad < newPad)
440: enhancer.addNulls(_src + 1, newPad - oldPad);
441: }
442: }
443:
444: void removePad(CodeEnhancer enhancer, int offset, int count) {
445: // offset is before the jump
446: if (_oldSrc != _src) {
447: int oldPad = (4 - (_oldSrc + 1) % 4) % 4;
448: int newPad = (4 - (_src + 1) % 4) % 4;
449:
450: _oldSrc = _src;
451:
452: if (newPad < oldPad)
453: enhancer.remove(_src + 1, oldPad - newPad);
454: else if (oldPad < newPad)
455: enhancer.addNulls(_src + 1, newPad - oldPad);
456: }
457: }
458:
459: public boolean equals(Object v) {
460: if (!(v instanceof Switch))
461: return false;
462:
463: Switch s = (Switch) v;
464:
465: return _src == s._src;
466: }
467: }
468:
469: static class TableSwitch extends Switch {
470: TableSwitch(int src, CodeVisitor visitor) {
471: super (src);
472:
473: int arg = src + 1;
474: arg += (4 - arg % 4) % 4;
475:
476: int low = visitor.getInt(arg + 4);
477: int high = visitor.getInt(arg + 8);
478:
479: int[] offsets = new int[high - low + 2];
480:
481: offsets[0] = arg;
482:
483: for (int i = 0; i <= high - low; i++) {
484: offsets[i + 1] = arg + 12 + i * 4;
485: }
486:
487: setOffsets(offsets);
488: }
489: }
490:
491: static class LookupSwitch extends Switch {
492: LookupSwitch(int src, CodeVisitor visitor) {
493: super (src);
494:
495: int arg = src + 1;
496: arg += (4 - arg % 4) % 4;
497:
498: int n = visitor.getInt(arg + 4);
499:
500: int[] offsets = new int[n + 1];
501: offsets[0] = arg;
502:
503: for (int i = 0; i < n; i++) {
504: offsets[i + 1] = arg + 8 + i * 8 + 4;
505: }
506:
507: setOffsets(offsets);
508: }
509: }
510:
511: class JumpAnalyzer extends Analyzer {
512: public void analyze(CodeVisitor visitor) throws Exception {
513: if (visitor.isSwitch()) {
514: int src = visitor.getOffset();
515:
516: switch (visitor.getOpcode()) {
517: case TABLESWITCH: {
518: TableSwitch branch = new TableSwitch(src, visitor);
519: if (!_switches.contains(branch))
520: _switches.add(branch);
521: break;
522: }
523:
524: case LOOKUPSWITCH: {
525: LookupSwitch branch = new LookupSwitch(src, visitor);
526: if (!_switches.contains(branch))
527: _switches.add(branch);
528: break;
529: }
530: }
531: } else if (visitor.isBranch()) {
532: int src = visitor.getOffset();
533: int offset = visitor.getShortArg(1);
534:
535: _jumps.add(new Jump(src, offset));
536: }
537: }
538: }
539: }
|