1 /*
2 * The Apache Software License, Version 1.1
3 *
4 * Copyright (c) 2002 The Apache Software Foundation. All rights
5 * reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The end-user documentation included with the redistribution, if
20 * any, must include the following acknowlegement:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowlegement may appear in the software itself,
24 * if and wherever such third-party acknowlegements normally appear.
25 *
26 * 4. The names "Apache BSF", "Apache", and "Apache Software Foundation"
27 * must not be used to endorse or promote products derived from
28 * this software without prior written permission. For written
29 * permission, please contact apache@apache.org.
30 *
31 * 5. Products derived from this software may not be called "Apache"
32 * nor may "Apache" appear in their names without prior written
33 * permission of the Apache Group.
34 *
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many individuals
50 * on behalf of the Apache Software Foundation and was originally created by
51 * Sanjiva Weerawarana and others at International Business Machines
52 * Corporation. For more information on the Apache Software Foundation,
53 * please see <http://www.apache.org/>.
54 */
55
56 package org.apache.bsf.engines.javascript;
57
58 import java.io.StringReader;
59 import java.io.InputStream;
60 import java.io.IOException;
61 import java.lang.reflect.InvocationTargetException;
62 import java.util.Vector;
63 import java.util.Enumeration;
64
65 import org.mozilla.javascript;
66 import org.mozilla.javascript.debug;
67
68 import org.apache.bsf;
69 import org.apache.bsf.util.BSFEngineImpl;
70 import org.apache.bsf.util.BSFFunctions;
71
72 import java.util.Hashtable;
73 import org.apache.bsf.debug.jsdi;
74
75 import java.io.Reader;
76
77 /**
78 * A document cell materializes a known document.
79 * A document is a container for scripts or functions
80 * in JavaScript. The document is known as soon as
81 * a function or script is compiled in the engine.
82 * Compilation occurs as a side-effect of evaluating
83 * or executing a function or a script.
84 *
85 * Upon the first loading of a function or script of
86 * a document, the document becomes known and the debug
87 * manager is notified of the load. The debug manager
88 * will in turn notify the engine of all the known
89 * breakpoints for that document.
90 *
91 * When a breakpoint is propagated from the debug manager
92 * to an engine, the document will be added a breakpoint.
93 * The document will memorize the breakpoints if their
94 * corresponding function or script is not known at that
95 * time. If it is known, it is the FnOrScript that will
96 * memorize the breakpoint. See FnOrScript to see how
97 * a breakpoint is actually forwarded to the underlying
98 * Rhino engine.
99 *
100 */
101 public class DocumentCell {
102
103 RhinoEngineDebugger m_rhinoDebugger;
104
105 String m_docName;
106 Vector m_fnOrScripts;
107 Vector m_breakpoints;
108 private boolean m_entryexit;
109 private FnOrScript m_lastFnOrScript;
110
111 Hashtable m_functionMap;
112
113 public DocumentCell(RhinoEngineDebugger rhinoDebugger, String name) {
114 m_rhinoDebugger = rhinoDebugger;
115 m_docName = name;
116 m_breakpoints = new Vector();
117 m_functionMap = new Hashtable();
118 m_fnOrScripts = new Vector();
119 m_entryexit = false;
120 m_lastFnOrScript = null;
121 }
122
123 public String getName() {
124 return m_docName;
125 }
126
127 /**
128 * Add a breakpoint.
129 * Two cases exist.
130 * If a function or a script (FnOrScript) is known for
131 * the given line number, the breakpoint will be remembered
132 * by that FnOrScript.
133 * Otherwise, the breakpoint is memorized at the document
134 * level until a function or script is known, that is,
135 * compiled in our engine.
136 *
137 */
138 public void addBreakpointAtLine(int brkptId, int lineno) {
139 Enumeration e;
140 FnOrScript fnOrScript;
141
142 BreakPoint bp = new BreakPoint(this, brkptId);
143 bp.setLineNo(lineno);
144
145 // Propagate the breakpoint at document level
146 // to the level of already known function or
147 // scripts. It will propagate it down to the
148 // known compilation units.
149 e = m_fnOrScripts.elements();
150 while (e.hasMoreElements()) {
151 fnOrScript = (FnOrScript) e.nextElement();
152 try {
153 if (fnOrScript.contains(bp)) {
154 fnOrScript.addBreakpoint(bp);
155 return;
156 }
157 } catch (BSFException ex) {
158 }
159 }
160 m_breakpoints.addElement(bp);
161 }
162
163 /**
164 * Same as above, except the breakpoint is specified
165 * at an character offset rather than a line number.
166 */
167 public void addBreakpointAtOffset(int brkptId, int offset) {
168 Enumeration e;
169 FnOrScript fnOrScript;
170
171 BreakPoint bp = new BreakPoint(this, brkptId);
172 bp.setOffset(offset);
173
174 e = m_fnOrScripts.elements();
175 while (e.hasMoreElements()) {
176 fnOrScript = (FnOrScript) e.nextElement();
177 try {
178 if (fnOrScript.contains(bp)) {
179 fnOrScript.addBreakpoint(bp);
180 return;
181 }
182 } catch (BSFException ex) {
183 }
184 }
185 m_breakpoints.addElement(bp);
186 }
187
188 //-----------------------------------------
189 // Check to see if we have pending breakpoints
190 // at the document level that belong to this
191 // Function or Script.
192 private void attachBreakpoints(FnOrScript fnOrScript) {
193
194 Enumeration e;
195 BreakPoint bp;
196 Vector toremove = new Vector();
197 e = m_breakpoints.elements();
198 while (e.hasMoreElements()) {
199 bp = (BreakPoint) e.nextElement();
200 try {
201 if (fnOrScript.contains(bp)) {
202 // we got a pending breakpoint...
203 // add it to the compilation unit and remember it
204 // to remove it later.
205 fnOrScript.addBreakpoint(bp);
206 toremove.addElement(bp);
207 }
208 } catch (BSFException ex) {
209 }
210 }
211 // now that we are doning iterating over breakpoints,
212 // we can remove all the ones that need to be removed.
213 e = toremove.elements();
214 while (e.hasMoreElements()) {
215 bp = (BreakPoint) e.nextElement();
216 m_breakpoints.removeElement(bp);
217 }
218 }
219
220 public BreakPoint findBreakpointAtLine(int lineno) throws BSFException {
221 Enumeration e;
222 BreakPoint bp;
223 FnOrScript fnOrScript;
224
225 e = m_fnOrScripts.elements();
226 while (e.hasMoreElements()) {
227 fnOrScript = (FnOrScript) e.nextElement();
228 bp = fnOrScript.findBreakpointAtLine(lineno);
229 if (bp != null)
230 return bp;
231 }
232 return null;
233 }
234
235 public BreakPoint findBreakpointAtOffset(int offset) throws BSFException {
236 Enumeration e;
237 BreakPoint bp;
238 FnOrScript fnOrScript;
239
240 e = m_fnOrScripts.elements();
241 while (e.hasMoreElements()) {
242 fnOrScript = (FnOrScript) e.nextElement();
243 bp = fnOrScript.findBreakpointAtOffset(offset);
244 if (bp != null)
245 return bp;
246 }
247 return null;
248 }
249
250 public FnOrScript findFnOrScript(int startLine, int column) {
251 Enumeration e;
252 FnOrScript fnOrScript;
253 e = m_fnOrScripts.elements();
254 while (e.hasMoreElements()) {
255 fnOrScript = (FnOrScript) e.nextElement();
256 if (fnOrScript.m_startLine == startLine)
257 if (fnOrScript.m_column == column)
258 return fnOrScript;
259 }
260 return null;
261 }
262
263 public FnOrScript findFnOrScriptContaining(int line) {
264 Enumeration e;
265 FnOrScript fnOrScript;
266 e = m_fnOrScripts.elements();
267 while (e.hasMoreElements()) {
268 fnOrScript = (FnOrScript) e.nextElement();
269 if (fnOrScript.m_startLine <= line &&
270 (fnOrScript.m_startLine + fnOrScript.m_lineCount) >= line)
271 return fnOrScript;
272 }
273 return null;
274 }
275
276 public Enumeration fnOrScripts() {
277 return m_fnOrScripts.elements();
278 }
279
280 public FnOrScript registerFnOrScriptLines(Reader reader,
281 int startLine,
282 int column)
283 throws BSFException {
284
285 FnOrScript fnOrScript;
286 Enumeration e;
287 // first, search if we already have the script or function.
288 e = m_fnOrScripts.elements();
289 while (e.hasMoreElements()) {
290 fnOrScript = (FnOrScript) e.nextElement();
291 if (fnOrScript.getFirstLine() == startLine)
292 if (fnOrScript.getColumn() == column)
293 return fnOrScript;
294 }
295 try {
296
297 fnOrScript = new FnOrScript(this);
298 m_fnOrScripts.addElement(fnOrScript);
299
300 fnOrScript.specifyLinesPos(reader, startLine, column);
301
302 this.attachBreakpoints(fnOrScript);
303 } catch (IOException ex) {
304 throw new BSFException(
305 BSFException.REASON_EXECUTION_ERROR,
306 "while registering script or function.",
307 ex);
308 }
309 return fnOrScript;
310 }
311
312 public FnOrScript registerFnOrScriptLines(String source,
313 int startLine,
314 int column)
315 throws BSFException {
316 Reader reader = new StringReader(source);
317 return registerFnOrScriptLines(reader, startLine, column);
318 }
319
320 public FnOrScript registerFnOrScriptRange(Reader reader, int offset)
321 throws BSFException {
322
323 FnOrScript fnOrScript;
324 try {
325
326 fnOrScript = new FnOrScript(this);
327 m_fnOrScripts.addElement(fnOrScript);
328
329 fnOrScript.specifyRange(reader, offset);
330
331 this.attachBreakpoints(fnOrScript);
332 } catch (IOException ex) {
333 throw new BSFException(
334 BSFException.REASON_EXECUTION_ERROR,
335 "while registering script or function.",
336 ex);
337 }
338 return fnOrScript;
339 }
340
341 public FnOrScript registerFnOrScriptRange(String source, int offset)
342 throws BSFException {
343
344 Reader reader = new StringReader(source);
345 return registerFnOrScriptRange(reader, offset);
346 }
347
348 /**
349 * Removing a breakpoint.
350 * Two cases, a breakpoint is only remembered at
351 * the document level, it has not been propagated
352 * to a function or script (FnOrScript). Then, just
353 * drop it.
354 * Second case, the breakpoint has been propagated,
355 * then scan the FnOrScript objects and ask them
356 * to drop the breakpoint.
357 * Note: only one will have it, see addBreakpoint...
358 */
359 public BreakPoint removeBreakpoint(int brkptId) {
360 Enumeration e;
361 BreakPoint bp=null;
362 FnOrScript fnOrScript;
363
364 // search for the breakpoint to remove
365 // at the document level first.
366 e = m_breakpoints.elements();
367 while (e.hasMoreElements()) {
368 bp = (BreakPoint) e.nextElement();
369 if (bp.getId()==brkptId) {
370 // we found it, just drop it
371 // and return.
372 m_breakpoints.removeElement(bp);
373 return bp;
374 }
375 }
376 // the breakpoint has not been found at
377 // the document level. It must have been
378 // propagated at a FnOrScript level.
379 e = m_fnOrScripts.elements();
380 while (e.hasMoreElements()) {
381 fnOrScript = (FnOrScript) e.nextElement();
382 bp = fnOrScript.removeBreakpoint(brkptId);
383 if (null!=bp) break;
384 }
385 return bp;
386 }
387
388 public void setEntryExit(boolean on_value) {
389 m_entryexit = on_value;
390 }
391
392 public boolean getEntryExit() {
393 return m_entryexit;
394 }
395
396 public void setLastFnOrScript(FnOrScript fnos) {
397 m_lastFnOrScript = fnos;
398 }
399
400 public FnOrScript getLastFnOrScript() {
401 return m_lastFnOrScript;
402 }
403 }