001: /*
002: Copyright (c) 2004, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.classes;
030:
031: import org.apache.bcel.generic.BranchHandle;
032: import org.apache.bcel.generic.InstructionHandle;
033:
034: /**
035: * Wrapper for branch handle. This preserves a snapshot of the stack state for
036: * the branch instruction, matching it against the stack state for the target
037: * instruction when set.
038: *
039: * @author Dennis M. Sosnoski
040: * @version 1.0
041: */
042:
043: public class BranchWrapper {
044: /** Track source code location for generated branches. */
045: private static boolean s_trackSource;
046:
047: /** Continue on after code generation error flag. */
048: private static boolean s_errorOverride;
049:
050: /** Actual wrapped instruction handle. */
051: private final BranchHandle m_branchHandle;
052:
053: /** Stack state for branch origin. */
054: private final String[] m_stackTypes;
055:
056: /** Object that generated branch. */
057: private final Object m_sourceObject;
058:
059: /** Code generation backtrace for source of branch. */
060: private final Throwable m_sourceTrace;
061:
062: /**
063: * Constructor.
064: *
065: * @param hand branch handle
066: * @param types array of types of values on stack
067: * @param src object responsible for generating branch
068: */
069:
070: /*package*/BranchWrapper(BranchHandle hand, String[] types,
071: Object src) {
072: m_branchHandle = hand;
073: m_stackTypes = types;
074: m_sourceObject = src;
075: if (s_trackSource) {
076: m_sourceTrace = new Throwable();
077: } else {
078: m_sourceTrace = null;
079: }
080: }
081:
082: /**
083: * Get branch origin stack state information.
084: *
085: * @return array of types of values on stack
086: */
087:
088: /*package*/String[] getStackState() {
089: return m_stackTypes;
090: }
091:
092: /**
093: * Generate description of stack state.
094: *
095: * @param types array of types on stack
096: * @return stack state description
097: */
098:
099: private String describeStack(String[] types) {
100: StringBuffer buff = new StringBuffer();
101: for (int i = 0; i < types.length; i++) {
102: buff.append(" ");
103: buff.append(i);
104: buff.append(": ");
105: buff.append(types[i]);
106: buff.append('\n');
107: }
108: return buff.toString();
109: }
110:
111: /**
112: * Report branch target error. Dumps the stack trace for the source of the
113: * branch, if source tracking is enabled, and generates an exception that
114: * includes the stack state information.
115: *
116: * @param text basic error message text
117: * @param types stack state description
118: * @param mb method builder using this code
119: * @return complete error description text
120: */
121:
122: private String buildReport(String text, String[] types,
123: MethodBuilder mb) {
124:
125: // start by dumping branch generation source trace
126: if (m_sourceTrace != null) {
127: System.err.println("Backtrack for branch source:");
128: m_sourceTrace.printStackTrace(System.err);
129: }
130:
131: // generate error message leading text
132: StringBuffer buff = new StringBuffer(text);
133: buff.append("\n in method ");
134: buff.append(mb.getClassFile().getName());
135: buff.append('.');
136: buff.append(mb.getName());
137: buff.append("\n generated by ");
138: buff.append(m_sourceObject.toString());
139: buff.append("\n from stack:\n");
140: buff.append(describeStack(m_stackTypes));
141: buff.append(" to stack:\n");
142: buff.append(describeStack(types));
143: return buff.toString();
144: }
145:
146: /**
147: * Set target instruction for branch. Validates the branch source stack
148: * state against the branch target stack state.
149: *
150: * @param hand target branch instruction handle
151: * @param types stack state description
152: * @param mb method builder using this code
153: */
154:
155: /*package*/void setTarget(InstructionHandle hand, String[] types,
156: MethodBuilder mb) {
157:
158: // match stack states
159: if (types.length == m_stackTypes.length) {
160: for (int i = 0; i < types.length; i++) {
161: String stype = m_stackTypes[i];
162: if (!types[i].equals(stype)) {
163: if (!"<null>".equals(types[i])
164: && !"<null>".equals(stype)) {
165: if (m_sourceTrace != null) {
166: System.err
167: .println("Backtrack for branch source:");
168: m_sourceTrace.printStackTrace(System.err);
169: }
170: String text = buildReport(
171: "Stack value type mismatch on branch",
172: types, mb);
173: if (s_errorOverride) {
174: new Throwable(text)
175: .printStackTrace(System.err);
176: } else {
177: throw new IllegalStateException(text);
178: }
179: }
180: }
181: }
182: } else {
183: String text = buildReport("Stack size mismatch on branch",
184: types, mb);
185: if (s_errorOverride) {
186: new Throwable(text).printStackTrace(System.err);
187: } else {
188: throw new IllegalStateException(text);
189: }
190: }
191:
192: // set the branch target
193: m_branchHandle.setTarget(hand);
194: }
195:
196: /**
197: * Set target instruction for branch. Validates the branch source stack
198: * state against the branch target stack state.
199: *
200: * @param target branch target wrapper
201: * @param mb method builder using this code
202: */
203:
204: public void setTarget(BranchTarget target, MethodBuilder mb) {
205: setTarget(target.getInstruction(), target.getStack(), mb);
206: }
207:
208: /**
209: * Set branch code generation tracking state. When set, this saves a stack
210: * trace for each generated branch instruction, allowing the source of a
211: * branch to be traced when an error occurs in setting the branch target.
212: *
213: * @param track <code>true</code> to enable branch code generation tracking,
214: * <code>false</code> to disable it
215: */
216:
217: public static void setTracking(boolean track) {
218: s_trackSource = track;
219: }
220:
221: /**
222: * Set target setting error override state. When set, this blocks throwing
223: * an exception when an error occurs on setting the branch target, instead
224: * just printing the information to the console.
225: *
226: * @param over <code>true</code> to override exception on target error,
227: * <code>false</code> to allow it
228: */
229:
230: public static void setErrorOverride(boolean over) {
231: s_errorOverride = over;
232: }
233: }
|