/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jvnet.hudson.test.rhino;
import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import net.sourceforge.htmlunit.corejs.javascript.debug.DebugFrame;
import net.sourceforge.htmlunit.corejs.javascript.debug.DebuggableScript;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* Stack frame.
*
* @author Kohsuke Kawaguchi
*/
public class CallStackFrame implements DebugFrame {
/**
* {@link JavaScriptDebugger} that this stack frame lives in.
*/
public final JavaScriptDebugger owner;
/**
* The function being executed.
*/
public final DebuggableScript fnOrScript;
private Scriptable activation;
private Scriptable thisObj;
private Object[] args;
private int line;
public CallStackFrame(JavaScriptDebugger owner, DebuggableScript fnOrScript) {
this.owner = owner;
this.fnOrScript = fnOrScript;
}
public void onEnter(Context cx, Scriptable activation, Scriptable thisObj, Object[] args) {
this.activation = activation;
this.thisObj = thisObj;
this.args = args;
owner.callStack.add(this);
}
public void onExit(Context cx, boolean byThrow, Object resultOrException) {
// can't simply call removeFirst, because due to tail call elimination,
// intermediate frames can be dropped at any time
// TODO: shouldn't it be suffice to just check the end?
for( int i=owner.callStack.size()-1; i>=0; i-- )
if(owner.callStack.get(i)==this) {
owner.callStack.remove(i);
break;
}
activation = null;
thisObj = null;
args = null;
line = -1;
}
public void onLineChange(Context cx, int lineNumber) {
this.line = lineNumber;
}
public void onExceptionThrown(Context cx, Throwable ex) {
}
public void onDebuggerStatement(Context cx) {
}
/**
* In-scope variables.
*/
public SortedMap<String,Object> getVariables() {
SortedMap<String,Object> r = new TreeMap<String,Object>();
for( int i=fnOrScript.getParamAndVarCount()-1; i>=0; i-- ) {
String name =fnOrScript.getParamOrVarName(i);
r.put(name,activation.get(name,activation));
}
return r;
}
/**
* Formats this call stack, arguments, and its local variables as a human readable string.
*/
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(fnOrScript.getFunctionName());
buf.append('(');
for( int i=0; i<args.length; i++ ) {
if(i!=0) buf.append(',');
buf.append(args[i]);
}
buf.append(')');
buf.append("\n at ").append(fnOrScript.getSourceName()).append('#').append(line);
buf.append("\n variables=").append(getVariables());
return buf.toString();
}
}