001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.jjs.impl;
017:
018: import com.google.gwt.dev.jjs.SourceInfo;
019: import com.google.gwt.dev.jjs.ast.Context;
020: import com.google.gwt.dev.jjs.ast.JBlock;
021: import com.google.gwt.dev.jjs.ast.JExpression;
022: import com.google.gwt.dev.jjs.ast.JExpressionStatement;
023: import com.google.gwt.dev.jjs.ast.JIfStatement;
024: import com.google.gwt.dev.jjs.ast.JInstanceOf;
025: import com.google.gwt.dev.jjs.ast.JLocal;
026: import com.google.gwt.dev.jjs.ast.JLocalRef;
027: import com.google.gwt.dev.jjs.ast.JMethod;
028: import com.google.gwt.dev.jjs.ast.JMethodBody;
029: import com.google.gwt.dev.jjs.ast.JMethodCall;
030: import com.google.gwt.dev.jjs.ast.JModVisitor;
031: import com.google.gwt.dev.jjs.ast.JProgram;
032: import com.google.gwt.dev.jjs.ast.JReferenceType;
033: import com.google.gwt.dev.jjs.ast.JStatement;
034: import com.google.gwt.dev.jjs.ast.JThrowStatement;
035: import com.google.gwt.dev.jjs.ast.JTryStatement;
036:
037: import java.util.ArrayList;
038: import java.util.List;
039:
040: /**
041: * Merge multi-catch blocks into a single catch block that uses instanceof tests
042: * to determine which user block to run.
043: */
044: public class CatchBlockNormalizer {
045:
046: /**
047: * Collapses all multi-catch blocks into a single catch block.
048: */
049: private class CollapseCatchBlocks extends JModVisitor {
050:
051: // @Override
052: public void endVisit(JMethodBody x, Context ctx) {
053: clearLocals();
054: currentMethodBody = null;
055: }
056:
057: // @Override
058: public void endVisit(JTryStatement x, Context ctx) {
059: if (x.getCatchBlocks().isEmpty()) {
060: return;
061: }
062:
063: SourceInfo catchInfo = ((JBlock) x.getCatchBlocks().get(0))
064: .getSourceInfo();
065:
066: JLocal exObj = popTempLocal();
067: JLocalRef exRef = new JLocalRef(program, catchInfo, exObj);
068: JBlock newCatchBlock = new JBlock(program, catchInfo);
069: // $e = Exceptions.caught($e)
070: JMethod caughtMethod = program
071: .getIndexedMethod("Exceptions.caught");
072: JMethodCall call = new JMethodCall(program, catchInfo,
073: null, caughtMethod);
074: call.getArgs().add(exRef);
075: JExpressionStatement asg = program.createAssignmentStmt(
076: catchInfo, exRef, call);
077: newCatchBlock.statements.add(asg);
078:
079: /*
080: * Build up a series of if, else if statements to test the type of the
081: * exception object against the type of the user's catch block.
082: *
083: * Go backwards so we can nest the else statements in the correct order!
084: */
085: // rethrow the current exception if no one caught it
086: JStatement cur = new JThrowStatement(program, null, exRef);
087: for (int i = x.getCatchBlocks().size() - 1; i >= 0; --i) {
088: JBlock block = (JBlock) x.getCatchBlocks().get(i);
089: JLocalRef arg = (JLocalRef) x.getCatchArgs().get(i);
090: catchInfo = block.getSourceInfo();
091: JReferenceType argType = (JReferenceType) arg.getType();
092: // if ($e instanceof ArgType) { userVar = $e; <user code> }
093: JExpression ifTest = new JInstanceOf(program,
094: catchInfo, argType, exRef);
095: asg = program.createAssignmentStmt(catchInfo, arg,
096: exRef);
097: if (!block.statements.isEmpty()) {
098: // Only bother adding the assignment if the block is non-empty
099: block.statements.add(0, asg);
100: }
101: // nest the previous as an else for me
102: cur = new JIfStatement(program, catchInfo, ifTest,
103: block, cur);
104: }
105:
106: newCatchBlock.statements.add(cur);
107: x.getCatchArgs().clear();
108: x.getCatchArgs().add(exRef);
109: x.getCatchBlocks().clear();
110: x.getCatchBlocks().add(newCatchBlock);
111: }
112:
113: // @Override
114: public boolean visit(JMethodBody x, Context ctx) {
115: currentMethodBody = x;
116: clearLocals();
117: return true;
118: }
119:
120: // @Override
121: public boolean visit(JTryStatement x, Context ctx) {
122: if (!x.getCatchBlocks().isEmpty()) {
123: pushTempLocal();
124: }
125: return true;
126: }
127: }
128:
129: public static void exec(JProgram program) {
130: new CatchBlockNormalizer(program).execImpl();
131: }
132:
133: private JMethodBody currentMethodBody;
134: private int localIndex;
135: private final JProgram program;
136: private final List/* <JLocal> */tempLocals = new ArrayList/* <JLocal> */();
137:
138: private CatchBlockNormalizer(JProgram program) {
139: this .program = program;
140: }
141:
142: private void clearLocals() {
143: tempLocals.clear();
144: localIndex = 0;
145: }
146:
147: private void execImpl() {
148: CollapseCatchBlocks collapser = new CollapseCatchBlocks();
149: collapser.accept(program);
150: }
151:
152: private JLocal popTempLocal() {
153: return (JLocal) tempLocals.get(--localIndex);
154: }
155:
156: private void pushTempLocal() {
157: if (localIndex == tempLocals.size()) {
158: JLocal newTemp = program.createLocal(null,
159: ("$e" + localIndex).toCharArray(), program
160: .getTypeJavaLangObject(), false,
161: currentMethodBody);
162: tempLocals.add(newTemp);
163: }
164: ++localIndex;
165: }
166:
167: }
|