001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.nativequery.analysis;
022:
023: import java.util.*;
024:
025: import EDU.purdue.cs.bloat.cfg.*;
026: import EDU.purdue.cs.bloat.editor.*;
027: import EDU.purdue.cs.bloat.tree.*;
028:
029: import com.db4o.instrumentation.core.*;
030: import com.db4o.instrumentation.util.*;
031: import com.db4o.nativequery.*;
032: import com.db4o.nativequery.expr.*;
033: import com.db4o.nativequery.expr.build.*;
034: import com.db4o.nativequery.expr.cmp.*;
035: import com.db4o.nativequery.expr.cmp.operand.*;
036:
037: public class BloatExprBuilderVisitor extends TreeVisitor {
038:
039: // TODO discuss: drop or make configurable
040: private final static int MAX_DEPTH = 10;
041:
042: private final static String[] PRIMITIVE_WRAPPER_NAMES = {
043: Boolean.class.getName(), Byte.class.getName(),
044: Short.class.getName(), Character.class.getName(),
045: Integer.class.getName(), Long.class.getName(),
046: Double.class.getName(), Float.class.getName(),
047: String.class.getName(), Date.class.getName() };
048:
049: static {
050: Arrays.sort(PRIMITIVE_WRAPPER_NAMES);
051: }
052:
053: private final static ExpressionBuilder BUILDER = new ExpressionBuilder();
054:
055: private final static Map BUILDERS = new HashMap();
056:
057: private final static Map OP_SYMMETRY = new HashMap();
058:
059: private static class ComparisonBuilder {
060: private ComparisonOperator op;
061:
062: public ComparisonBuilder(ComparisonOperator op) {
063: this .op = op;
064: }
065:
066: public Expression buildComparison(FieldValue fieldValue,
067: ComparisonOperand valueExpr) {
068: if (isBooleanField(fieldValue)) {
069: if (valueExpr instanceof ConstValue) {
070: ConstValue constValue = (ConstValue) valueExpr;
071: if (constValue.value() instanceof Integer) {
072: Integer intValue = (Integer) constValue.value();
073: Boolean boolValue = (intValue.intValue() == 0 ? Boolean.FALSE
074: : Boolean.TRUE);
075: valueExpr = new ConstValue(boolValue);
076: }
077: }
078: }
079: return new ComparisonExpression(fieldValue, valueExpr, op);
080: }
081: }
082:
083: private static class NegateComparisonBuilder extends
084: ComparisonBuilder {
085: public NegateComparisonBuilder(ComparisonOperator op) {
086: super (op);
087: }
088:
089: public Expression buildComparison(FieldValue fieldValue,
090: ComparisonOperand valueExpr) {
091: return BUILDER.not(super .buildComparison(fieldValue,
092: valueExpr));
093: }
094: }
095:
096: private static class BuilderSpec {
097: private int _op;
098: private boolean _primitive;
099:
100: public BuilderSpec(int op, boolean primitive) {
101: this ._op = op;
102: this ._primitive = primitive;
103: }
104:
105: public int hashCode() {
106: final int prime = 31;
107: int result = 1;
108: result = prime * result + _op;
109: result = prime * result + (_primitive ? 1231 : 1237);
110: return result;
111: }
112:
113: public boolean equals(Object obj) {
114: if (this == obj)
115: return true;
116: if (obj == null)
117: return false;
118: if (getClass() != obj.getClass())
119: return false;
120: final BuilderSpec other = (BuilderSpec) obj;
121: if (_op != other._op)
122: return false;
123: if (_primitive != other._primitive)
124: return false;
125: return true;
126: }
127: }
128:
129: static {
130: BUILDERS.put(new BuilderSpec(IfStmt.EQ, false),
131: new ComparisonBuilder(ComparisonOperator.IDENTITY));
132: BUILDERS.put(new BuilderSpec(IfStmt.EQ, true),
133: new ComparisonBuilder(ComparisonOperator.EQUALS));
134: BUILDERS
135: .put(new BuilderSpec(IfStmt.NE, false),
136: new NegateComparisonBuilder(
137: ComparisonOperator.IDENTITY));
138: BUILDERS.put(new BuilderSpec(IfStmt.NE, true),
139: new NegateComparisonBuilder(ComparisonOperator.EQUALS));
140: BUILDERS.put(new BuilderSpec(IfStmt.LT, false),
141: new ComparisonBuilder(ComparisonOperator.SMALLER));
142: BUILDERS.put(new BuilderSpec(IfStmt.LT, true), builder(
143: IfStmt.LT, false));
144: BUILDERS.put(new BuilderSpec(IfStmt.GT, false),
145: new ComparisonBuilder(ComparisonOperator.GREATER));
146: BUILDERS.put(new BuilderSpec(IfStmt.GT, true), builder(
147: IfStmt.GT, false));
148: BUILDERS
149: .put(new BuilderSpec(IfStmt.LE, false),
150: new NegateComparisonBuilder(
151: ComparisonOperator.GREATER));
152: BUILDERS.put(new BuilderSpec(IfStmt.LE, true), builder(
153: IfStmt.LE, false));
154: BUILDERS
155: .put(new BuilderSpec(IfStmt.GE, false),
156: new NegateComparisonBuilder(
157: ComparisonOperator.SMALLER));
158: BUILDERS.put(new BuilderSpec(IfStmt.GE, true), builder(
159: IfStmt.GE, false));
160:
161: OP_SYMMETRY.put(new Integer(IfStmt.EQ), new Integer(IfStmt.EQ));
162: OP_SYMMETRY.put(new Integer(IfStmt.NE), new Integer(IfStmt.NE));
163: OP_SYMMETRY.put(new Integer(IfStmt.LT), new Integer(IfStmt.GT));
164: OP_SYMMETRY.put(new Integer(IfStmt.GT), new Integer(IfStmt.LT));
165: OP_SYMMETRY.put(new Integer(IfStmt.LE), new Integer(IfStmt.GE));
166: OP_SYMMETRY.put(new Integer(IfStmt.GE), new Integer(IfStmt.LE));
167: }
168:
169: private Expression expr;
170:
171: private Object retval;
172:
173: private Map seenBlocks = new HashMap();
174:
175: private BloatLoaderContext bloatUtil;
176:
177: private LinkedList methodStack = new LinkedList();
178:
179: private LinkedList localStack = new LinkedList();
180:
181: private int retCount = 0;
182:
183: private int blockCount = 0;
184:
185: public BloatExprBuilderVisitor(BloatLoaderContext bloatUtil) {
186: this .bloatUtil = bloatUtil;
187: localStack.addLast(new ComparisonOperand[] {
188: PredicateFieldRoot.INSTANCE,
189: CandidateFieldRoot.INSTANCE });
190: }
191:
192: private Object purgeReturnValue() {
193: Object expr = this .retval;
194: retval(null);
195: return expr;
196: }
197:
198: private void expression(Expression expr) {
199: retval(expr);
200: this .expr = expr;
201: }
202:
203: private void retval(Object expr) {
204: this .retval = expr;
205: }
206:
207: private static ComparisonBuilder builder(int op, boolean primitive) {
208: return (ComparisonBuilder) BUILDERS.get(new BuilderSpec(op,
209: primitive));
210: }
211:
212: public Expression expression() {
213: if (expr == null && isSingleReturn()
214: && retval instanceof ConstValue) {
215: expression(asExpression(retval));
216: }
217: return (checkComparisons(expr) ? expr : null);
218: }
219:
220: private boolean isSingleReturn() {
221: return retCount == 1 && blockCount == 4; // one plus source,init,sink
222: }
223:
224: private boolean checkComparisons(Expression expr) {
225: if (expr == null) {
226: return true;
227: }
228: final boolean[] result = { true };
229: ExpressionVisitor visitor = new TraversingExpressionVisitor() {
230: public void visit(ComparisonExpression expression) {
231: if (expression.left().root() != CandidateFieldRoot.INSTANCE) {
232: result[0] = false;
233: }
234: }
235: };
236: expr.accept(visitor);
237: return result[0];
238: }
239:
240: public void visitIfZeroStmt(IfZeroStmt stmt) {
241: stmt.expr().visit(this );
242: Object retval = purgeReturnValue();
243: boolean cmpNull = false;
244: if (retval instanceof FieldValue) {
245: // TODO: merge boolean and number primitive handling
246: Expression forced = identityOrBoolComparisonOrNull(retval);
247: if (forced != null) {
248: retval = forced;
249: } else {
250: FieldValue fieldVal = (FieldValue) retval;
251: String fieldType = (String) fieldVal.tag();
252: Object constVal = null;
253: if (fieldType.length() == 1) {
254: constVal = new Integer(0);
255: }
256: retval = new ComparisonExpression(fieldVal,
257: new ConstValue(constVal),
258: ComparisonOperator.EQUALS);
259: cmpNull = true;
260: }
261: }
262: if (retval instanceof Expression) {
263: Expression expr = (Expression) retval;
264: if (stmt.comparison() == IfStmt.EQ && !cmpNull
265: || stmt.comparison() == IfStmt.NE && cmpNull) {
266: expr = BUILDER.not(expr);
267: }
268: expression(buildComparison(stmt, expr));
269: return;
270: }
271: if (!(retval instanceof ThreeWayComparison)) {
272: throw new EarlyExitException();
273: }
274: ThreeWayComparison cmp = (ThreeWayComparison) retval;
275: Expression expr = null;
276: int comparison = stmt.comparison();
277: if (cmp.swapped()) {
278: comparison = ((Integer) OP_SYMMETRY.get(new Integer(
279: comparison))).intValue();
280: }
281: switch (comparison) {
282: case IfStmt.EQ:
283: expr = new ComparisonExpression(cmp.left(), cmp.right(),
284: ComparisonOperator.EQUALS);
285: break;
286: case IfStmt.NE:
287: expr = BUILDER.not(new ComparisonExpression(cmp.left(), cmp
288: .right(), ComparisonOperator.EQUALS));
289: break;
290: case IfStmt.LT:
291: expr = new ComparisonExpression(cmp.left(), cmp.right(),
292: ComparisonOperator.SMALLER);
293: break;
294: case IfStmt.GT:
295: expr = new ComparisonExpression(cmp.left(), cmp.right(),
296: ComparisonOperator.GREATER);
297: break;
298: case IfStmt.LE:
299: expr = BUILDER.not(new ComparisonExpression(cmp.left(), cmp
300: .right(), ComparisonOperator.GREATER));
301: break;
302: case IfStmt.GE:
303: expr = BUILDER.not(new ComparisonExpression(cmp.left(), cmp
304: .right(), ComparisonOperator.SMALLER));
305: break;
306: default:
307: break;
308: }
309: expression(buildComparison(stmt, expr));
310: }
311:
312: public void visitIfCmpStmt(IfCmpStmt stmt) {
313: stmt.left().visit(this );
314: Object left = purgeReturnValue();
315: stmt.right().visit(this );
316: Object right = purgeReturnValue();
317: int op = stmt.comparison();
318: if ((left instanceof ComparisonOperand)
319: && (right instanceof FieldValue)) {
320: FieldValue rightField = (FieldValue) right;
321: if (rightField.root() == CandidateFieldRoot.INSTANCE) {
322: Object swap = left;
323: left = right;
324: right = swap;
325: op = ((Integer) OP_SYMMETRY.get(new Integer(op)))
326: .intValue();
327: }
328: }
329: if (!(left instanceof FieldValue)
330: || !(right instanceof ComparisonOperand)) {
331: throw new EarlyExitException();
332: }
333: FieldValue fieldExpr = (FieldValue) left;
334: ComparisonOperand valueExpr = (ComparisonOperand) right;
335:
336: boolean isPrimitive = isPrimitiveExpr(stmt.left());
337: Expression cmp = buildComparison(stmt, builder(op, isPrimitive)
338: .buildComparison(fieldExpr, valueExpr));
339: expression(cmp);
340: }
341:
342: public void visitExprStmt(ExprStmt stmt) {
343: super .visitExprStmt(stmt);
344: }
345:
346: public void visitCallExpr(CallExpr expr) {
347: boolean isStatic = (expr instanceof CallStaticExpr);
348: if (!isStatic && expr.method().name().equals("<init>")) {
349: throw new EarlyExitException();
350: }
351: if (!isStatic && expr.method().name().equals("equals")) {
352: CallMethodExpr call = (CallMethodExpr) expr;
353: if (isPrimitiveWrapper(call.receiver().type())) {
354: processEqualsCall(call, ComparisonOperator.EQUALS);
355: }
356: return;
357: }
358: // FIXME
359: if (!isStatic && expr.method().name().equals("activate")) {
360: final String activatableName = "com.db4o.ta.Activatable";
361: try {
362: ClassEditor activateClazz = bloatUtil.classEditor(expr
363: .method().declaringClass());
364: Type[] interfaces = activateClazz.interfaces();
365: for (int interfaceIdx = 0; interfaceIdx < interfaces.length; interfaceIdx++) {
366: Type curInterface = interfaces[interfaceIdx];
367: if (activatableName.equals(BloatUtil
368: .normalizeClassName(curInterface))) {
369: return;
370: }
371: }
372: } catch (ClassNotFoundException e) {
373: e.printStackTrace();
374: }
375: }
376: if (expr.method().declaringClass().equals(Type.STRING)) {
377: if (applyStringHandling(expr)) {
378: return;
379: }
380: }
381: ComparisonOperandAnchor rcvRetval = null;
382: if (!isStatic) {
383: ((CallMethodExpr) expr).receiver().visit(this );
384: rcvRetval = (ComparisonOperandAnchor) purgeReturnValue();
385: }
386: if (isPrimitiveWrapper(expr.method().declaringClass())) {
387: if (applyPrimitiveWrapperHandling(expr, rcvRetval)) {
388: return;
389: }
390: }
391: MemberRef methodRef = expr.method();
392: if (methodStack.contains(methodRef)
393: || methodStack.size() > MAX_DEPTH) {
394: throw new EarlyExitException();
395: }
396: methodStack.addLast(methodRef);
397: boolean addedLocals = false;
398: try {
399: List params = new ArrayList(expr.params().length + 1);
400: params.add(rcvRetval);
401: for (int idx = 0; idx < expr.params().length; idx++) {
402: expr.params()[idx].visit(this );
403: ComparisonOperand curparam = (ComparisonOperand) purgeReturnValue();
404: if ((curparam instanceof ComparisonOperandAnchor)
405: && (((ComparisonOperandAnchor) curparam).root() == CandidateFieldRoot.INSTANCE)) {
406: throw new EarlyExitException();
407: }
408: params.add(curparam);
409: }
410: addedLocals = true;
411: localStack.addLast(params
412: .toArray(new ComparisonOperand[params.size()]));
413:
414: if (rcvRetval == null
415: || rcvRetval.root() != CandidateFieldRoot.INSTANCE) {
416: if (rcvRetval == null) {
417: rcvRetval = new StaticFieldRoot(BloatUtil
418: .normalizeClassName(expr.method()
419: .declaringClass()));
420: }
421: params.remove(0);
422: Type[] paramTypes = expr.method().nameAndType().type()
423: .paramTypes();
424: Class[] javaParamTypes = new Class[paramTypes.length];
425: for (int paramIdx = 0; paramIdx < paramTypes.length; paramIdx++) {
426: String className = BloatUtil
427: .normalizeClassName(paramTypes[paramIdx]);
428: javaParamTypes[paramIdx] = (PRIMITIVE_CLASSES
429: .containsKey(className) ? (Class) PRIMITIVE_CLASSES
430: .get(className)
431: : Class.forName(className));
432: }
433: retval(new MethodCallValue(rcvRetval, expr.method()
434: .name(), javaParamTypes,
435: (ComparisonOperand[]) params
436: .toArray(new ComparisonOperand[params
437: .size()])));
438: return;
439: }
440:
441: Type declaringClass = methodRef.declaringClass();
442: // Nice try, but doesn't help, since the receiver's type always seems to be reported as java.lang.Object.
443: if (expr instanceof CallMethodExpr) {
444: Expr receiverExpr = ((CallMethodExpr) expr).receiver();
445: Type receiverType = receiverExpr.type();
446: if (isSuperType(declaringClass, receiverType)) {
447: declaringClass = receiverType;
448: }
449: }
450: FlowGraph flowGraph = bloatUtil.flowGraph(declaringClass
451: .className(), methodRef.name(), methodRef.type()
452: .paramTypes());
453: if (flowGraph == null) {
454: throw new EarlyExitException();
455: }
456: if (NQDebug.LOG) {
457: System.out.println("METHOD:"
458: + flowGraph.method().nameAndType());
459: flowGraph.visit(new PrintVisitor());
460: }
461: flowGraph.visit(this );
462: Object methodRetval = purgeReturnValue();
463: if (methodRetval == null) {
464: throw new EarlyExitException();
465: }
466: retval(methodRetval);
467: } catch (ClassNotFoundException e) {
468: e.printStackTrace();
469: } finally {
470: if (addedLocals) {
471: localStack.removeLast();
472: }
473: Object last = methodStack.removeLast();
474: if (!last.equals(methodRef)) {
475: throw new RuntimeException(
476: "method stack inconsistent: push=" + methodRef
477: + " , pop=" + last);
478: }
479: }
480: }
481:
482: private boolean isSuperType(Type declaringClass, Type receiverType)
483: throws ClassNotFoundException {
484: if (declaringClass.className().equals(receiverType.className())) {
485: return false;
486: }
487: ClassEditor receiverEditor = bloatUtil.classEditor(receiverType
488: .className());
489: Type super Class = receiverEditor.super class();
490: if (super Class != null) {
491: if (super Class.className().equals(
492: declaringClass.className())) {
493: return true;
494: }
495: if (isSuperType(declaringClass, super Class)) {
496: return true;
497: }
498: }
499: Type[] interfaces = receiverEditor.interfaces();
500: for (int interfaceIdx = 0; interfaceIdx < interfaces.length; interfaceIdx++) {
501: if (interfaces[interfaceIdx].className().equals(
502: declaringClass.className())) {
503: return true;
504: }
505: if (isSuperType(declaringClass, interfaces[interfaceIdx])) {
506: return true;
507: }
508: }
509: return false;
510: }
511:
512: private boolean applyPrimitiveWrapperHandling(CallExpr expr,
513: ComparisonOperandAnchor rcvRetval) {
514: String methodName = expr.method().name();
515: if (methodName.endsWith("Value")) {
516: return handlePrimitiveWrapperValueCall(rcvRetval);
517: }
518: if (methodName.equals("compareTo")) {
519: return handlePrimitiveWrapperCompareToCall(expr, rcvRetval);
520: }
521: return false;
522: }
523:
524: private boolean handlePrimitiveWrapperCompareToCall(CallExpr expr,
525: ComparisonOperandAnchor rcvRetval) {
526: ComparisonOperand left = rcvRetval;
527: expr.params()[0].visit(this );
528: ComparisonOperand right = (ComparisonOperand) purgeReturnValue();
529: retval(new ThreeWayComparison((FieldValue) left, right, false));
530: return true;
531: }
532:
533: private boolean handlePrimitiveWrapperValueCall(
534: ComparisonOperandAnchor rcvRetval) {
535: retval(rcvRetval);
536: if (rcvRetval instanceof FieldValue) {
537: FieldValue fieldval = (FieldValue) rcvRetval;
538: if (isBooleanField(fieldval)) {
539: retval(new ComparisonExpression(fieldval,
540: new ConstValue(Boolean.TRUE),
541: ComparisonOperator.EQUALS));
542: }
543: if (fieldval.root().equals(CandidateFieldRoot.INSTANCE)) {
544: return true;
545: }
546: }
547: return false;
548: }
549:
550: private boolean applyStringHandling(CallExpr expr) {
551: if (expr.method().name().equals("contains")) {
552: processEqualsCall((CallMethodExpr) expr,
553: ComparisonOperator.CONTAINS);
554: return true;
555: }
556: if (expr.method().name().equals("startsWith")) {
557: processEqualsCall((CallMethodExpr) expr,
558: ComparisonOperator.STARTSWITH);
559: return true;
560: }
561: if (expr.method().name().equals("endsWith")) {
562: processEqualsCall((CallMethodExpr) expr,
563: ComparisonOperator.ENDSWITH);
564: return true;
565: }
566: return false;
567: }
568:
569: private final static Map PRIMITIVE_CLASSES;
570:
571: static {
572: PRIMITIVE_CLASSES = new HashMap();
573: PRIMITIVE_CLASSES.put("Z", Boolean.TYPE);
574: PRIMITIVE_CLASSES.put("B", Byte.TYPE);
575: PRIMITIVE_CLASSES.put("S", Short.TYPE);
576: PRIMITIVE_CLASSES.put("C", Character.TYPE);
577: PRIMITIVE_CLASSES.put("I", Integer.TYPE);
578: PRIMITIVE_CLASSES.put("J", Long.TYPE);
579: PRIMITIVE_CLASSES.put("F", Float.TYPE);
580: PRIMITIVE_CLASSES.put("D", Double.TYPE);
581: }
582:
583: private boolean isPrimitiveWrapper(Type type) {
584: return Arrays.binarySearch(PRIMITIVE_WRAPPER_NAMES, BloatUtil
585: .normalizeClassName(type)) >= 0;
586: }
587:
588: private boolean isPrimitiveExpr(Expr expr) {
589: return expr.type().isPrimitive();
590: }
591:
592: private void processEqualsCall(CallMethodExpr expr,
593: ComparisonOperator op) {
594: Expr left = expr.receiver();
595: Expr right = expr.params()[0];
596: if (!isComparableExprOperand(left)
597: || !isComparableExprOperand(right)) {
598: throw new EarlyExitException();
599: }
600: left.visit(this );
601: Object leftObj = purgeReturnValue();
602: if (!(leftObj instanceof ComparisonOperand)) {
603: throw new EarlyExitException();
604: }
605: ComparisonOperand leftOp = (ComparisonOperand) leftObj;
606: right.visit(this );
607: ComparisonOperand rightOp = (ComparisonOperand) purgeReturnValue();
608: if (op.isSymmetric() && isCandidateFieldValue(rightOp)
609: && !isCandidateFieldValue(leftOp)) {
610: ComparisonOperand swap = leftOp;
611: leftOp = rightOp;
612: rightOp = swap;
613: }
614: if (!isCandidateFieldValue(leftOp) || rightOp == null) {
615: throw new EarlyExitException();
616: }
617: expression(new ComparisonExpression((FieldValue) leftOp,
618: rightOp, op));
619: }
620:
621: private boolean isCandidateFieldValue(ComparisonOperand op) {
622: return ((op instanceof FieldValue) && ((FieldValue) op).root() == CandidateFieldRoot.INSTANCE);
623: }
624:
625: private boolean isComparableExprOperand(Expr expr) {
626: return (expr instanceof FieldExpr)
627: || (expr instanceof StaticFieldExpr)
628: || (expr instanceof CallMethodExpr)
629: || (expr instanceof CallStaticExpr)
630: || (expr instanceof ConstantExpr)
631: || (expr instanceof LocalExpr);
632: }
633:
634: public void visitFieldExpr(FieldExpr expr) {
635: expr.object().visit(this );
636: Object fieldObj = purgeReturnValue();
637: String fieldName = expr.field().name();
638: if (fieldObj instanceof ComparisonOperandAnchor) {
639: retval(new FieldValue((ComparisonOperandAnchor) fieldObj,
640: fieldName, BloatUtil.normalizeClassName(expr
641: .field().type())));
642: }
643: }
644:
645: public void visitStaticFieldExpr(StaticFieldExpr expr) {
646: MemberRef field = expr.field();
647: retval(new FieldValue(new StaticFieldRoot(BloatUtil
648: .normalizeClassName(field.declaringClass())), field
649: .name(), BloatUtil.normalizeClassName(field.type())));
650: }
651:
652: public void visitConstantExpr(ConstantExpr expr) {
653: super .visitConstantExpr(expr);
654: retval(new ConstValue(expr.value()));
655: }
656:
657: public void visitLocalExpr(LocalExpr expr) {
658: super .visitLocalExpr(expr);
659: ComparisonOperand[] locals = (ComparisonOperand[]) localStack
660: .getLast();
661: if (expr.index() >= locals.length) {
662: throw new EarlyExitException();
663: }
664: retval(locals[expr.index()]);
665: }
666:
667: public void visitBlock(Block block) {
668: if (!seenBlocks.containsKey(block)) {
669: super .visitBlock(block);
670: seenBlocks.put(block, retval);
671: blockCount++;
672: } else {
673: retval(seenBlocks.get(block));
674: }
675: }
676:
677: public void visitFlowGraph(FlowGraph graph) {
678: try {
679: super .visitFlowGraph(graph);
680: if (expr == null) {
681: Expression forced = identityOrBoolComparisonOrNull(retval);
682: if (forced != null) {
683: expression(forced);
684: }
685: }
686: } catch (EarlyExitException exc) {
687: expression(null);
688: }
689: }
690:
691: private Expression identityOrBoolComparisonOrNull(Object val) {
692: if (val instanceof Expression) {
693: return (Expression) val;
694: }
695: if (!(val instanceof FieldValue)) {
696: return null;
697: }
698: FieldValue fieldVal = (FieldValue) val;
699: if (fieldVal.root() != CandidateFieldRoot.INSTANCE) {
700: return null;
701: }
702: String fieldType = ((String) fieldVal.tag());
703: if (fieldType.length() != 1) {
704: return null;
705: }
706: Object constVal = null;
707: switch (fieldType.charAt(0)) {
708: case 'Z':
709: constVal = Boolean.TRUE;
710: break;
711: // case 'I':
712: // constVal = new Integer(0);
713: // break;
714: default:
715: return null;
716: }
717: return new ComparisonExpression(fieldVal, new ConstValue(
718: constVal), ComparisonOperator.EQUALS);
719: }
720:
721: private static boolean isBooleanField(FieldValue fieldVal) {
722: return isFieldType(fieldVal, "Z")
723: || isFieldType(fieldVal, Boolean.class.getName());
724: }
725:
726: private boolean isIntField(FieldValue fieldVal) {
727: return isFieldType(fieldVal, "I");
728: }
729:
730: private static boolean isFieldType(FieldValue fieldVal,
731: String expType) {
732: return expType.equals(fieldVal.tag());
733: }
734:
735: public void visitArithExpr(ArithExpr expr) {
736: expr.left().visit(this );
737: Object leftObj = purgeReturnValue();
738: if (!(leftObj instanceof ComparisonOperand)) {
739: throw new EarlyExitException();
740: }
741: ComparisonOperand left = (ComparisonOperand) leftObj;
742: expr.right().visit(this );
743: Object rightObj = purgeReturnValue();
744: if (!(rightObj instanceof ComparisonOperand)) {
745: throw new EarlyExitException();
746: }
747: ComparisonOperand right = (ComparisonOperand) rightObj;
748: boolean swapped = false;
749: if (right instanceof FieldValue) {
750: FieldValue rightField = (FieldValue) right;
751: if (rightField.root() == CandidateFieldRoot.INSTANCE) {
752: ComparisonOperand swap = left;
753: left = right;
754: right = swap;
755: swapped = true;
756: }
757: }
758: switch (expr.operation()) {
759: case ArithExpr.ADD:
760: case ArithExpr.SUB:
761: case ArithExpr.MUL:
762: case ArithExpr.DIV:
763: retval(new ArithmeticExpression(left, right,
764: arithmeticOperator(expr.operation())));
765: break;
766: case ArithExpr.CMP:
767: case ArithExpr.CMPG:
768: case ArithExpr.CMPL:
769: if (left instanceof FieldValue) {
770: retval(new ThreeWayComparison((FieldValue) left, right,
771: swapped));
772: }
773: break;
774: case ArithExpr.XOR:
775: if (left instanceof FieldValue) {
776: retval(BUILDER.not(new ComparisonExpression(
777: (FieldValue) left, right,
778: ComparisonOperator.EQUALS)));
779: }
780: break;
781: default:
782: break;
783: }
784: }
785:
786: public void visitArrayRefExpr(ArrayRefExpr expr) {
787: expr.array().visit(this );
788: ComparisonOperandAnchor arrayOp = (ComparisonOperandAnchor) purgeReturnValue();
789: expr.index().visit(this );
790: ComparisonOperand idxOp = (ComparisonOperand) purgeReturnValue();
791: if (arrayOp == null || idxOp == null
792: || arrayOp.root() == CandidateFieldRoot.INSTANCE) {
793: throw new EarlyExitException();
794: }
795: retval(new ArrayAccessValue(arrayOp, idxOp));
796: }
797:
798: public void visitReturnExprStmt(ReturnExprStmt stat) {
799: stat.expr().visit(this );
800: retCount++;
801: }
802:
803: private ArithmeticOperator arithmeticOperator(int bloatOp) {
804: switch (bloatOp) {
805: case ArithExpr.ADD:
806: return ArithmeticOperator.ADD;
807: case ArithExpr.SUB:
808: return ArithmeticOperator.SUBTRACT;
809: case ArithExpr.MUL:
810: return ArithmeticOperator.MULTIPLY;
811: case ArithExpr.DIV:
812: return ArithmeticOperator.DIVIDE;
813: default:
814: return null;
815: }
816: }
817:
818: private Expression buildComparison(IfStmt stmt, Expression cmp) {
819: stmt.trueTarget().visit(this );
820: Object trueVal = purgeReturnValue();
821: stmt.falseTarget().visit(this );
822: Object falseVal = purgeReturnValue();
823: Expression trueExpr = asExpression(trueVal);
824: Expression falseExpr = asExpression(falseVal);
825: if (trueExpr == null || falseExpr == null) {
826: return null;
827: }
828: return BUILDER.ifThenElse(cmp, trueExpr, falseExpr);
829: }
830:
831: private Expression asExpression(Object obj) {
832: if (obj instanceof Expression) {
833: return (Expression) obj;
834: }
835: if (obj instanceof ConstValue) {
836: Object val = ((ConstValue) obj).value();
837: return asExpression(val);
838: }
839: if (obj instanceof Boolean) {
840: return BoolConstExpression.expr(((Boolean) obj)
841: .booleanValue());
842: }
843: if (obj instanceof Integer) {
844: int exprval = ((Integer) obj).intValue();
845: if (exprval == 0 || exprval == 1) {
846: return BoolConstExpression.expr(exprval == 1);
847: }
848: }
849: return null;
850: }
851:
852: public void visitStoreExpr(StoreExpr expr) {
853: if (!(expr.target() instanceof StackExpr)) {
854: throw new EarlyExitException();
855: }
856: super .visitStoreExpr(expr);
857: }
858:
859: private static class EarlyExitException extends RuntimeException {
860: }
861: }
|