/******************************************************************************* * * Copyright (c) 2004-2009 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi * * *******************************************************************************/ 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(); } }