package xtc.lang.blink;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import xtc.tree.GNode;
import xtc.tree.Visitor;
import xtc.lang.jeannie.Utilities;
import xtc.lang.blink.CommandAstAnalyzer.Language;
import xtc.lang.blink.CallStack.ICallFrame;
import xtc.lang.blink.CallStack.JavaCallFrame;
import xtc.lang.blink.CallStack.JeannieCallFrame;
import xtc.lang.blink.CallStack.NativeCallFrame;
import xtc.lang.blink.SymbolMapper.SourceFileAndLine;
import xtc.lang.blink.SymbolMapper.VariableRemapEntry;
import xtc.lang.blink.Event.Java2NativeReturnEvent;
import xtc.lang.blink.Event.JavaBreakPointHitEvent;
import xtc.lang.blink.Event.JavaStepCompletionEvent;
import xtc.lang.blink.Event.Native2JavaReturnEvent;
import xtc.lang.blink.Event.NativeBreakPointHitEvent;
import xtc.lang.blink.Event.NativeStepCompletionEvent;
import xtc.lang.blink.Event.SessionFinishRequestEvent;
/**
* The Blink user command line interpreter. See Debugger.rats, DebuggerC.rats
* and DebuggerJava.rats for formal syntax.
*
* @author Byeongcheol Lee
*/
public final class CommandInterpreter extends Visitor {
/**
* The blink debugger for the communication with jdb, gdb and the Blink Debug
* Agent (BDA).
*/
private final Blink dbg;
/** The break point manager. */
private final BreakPointManager breakpointManager;
/** The Jeannie symbol remapper. */
private final SymbolMapper jeannieSymbolRemapper;
/** The current context for debugging. */
private DebuggerContext debuggerContext;
/**
* Constructor.
*
* @param dbg The blink debugger.
* @param breakpointManager The break point manager.
*/
CommandInterpreter(final Blink dbg, BreakPointManager breakpointManager) {
this.dbg = dbg;
this.breakpointManager = breakpointManager;
this.jeannieSymbolRemapper = new SymbolMapper();
}
/** Implement BlinkEventSource. */
public String getEventSourceName() {
return "CommandRunner";
}
/** Visit an InitJCommand = (no children). */
public final void visitInitJCommand(final GNode n) {
dbg.initj();
dbg.ensurePureContext();
}
/** Visit a J2cCommand = (no children). */
public final void visitJ2cCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
dbg.j2c();
}
/** Visit a C2jCommand = (no children). */
public final void visitC2jCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
dbg.c2j();
}
/** Visit a JRetCommand = (no children). */
public final void visitJRetCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
dbg.jret();
}
/** Visit a JdbCommand = RestOfLine. */
public final void visitJdbCommand(final GNode n) {
String cmd = n.getString(0);
try {
String output = dbg.jdb.runCommand(cmd);
if (dbg.options.getVerboseLevel() < 1) {
dbg.out(output);
}
} catch (IOException e) {
dbg.err("can not successfully run the jdb command");
}
}
/** Visit a GdbCommand = RestOfLine. */
public final void visitGdbCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.out("gdb is not activated yet.\n");
return;
}
String cmd = n.getString(0);
try {
String output = dbg.ndb.runCommand(cmd);
if (dbg.options.getVerboseLevel() < 1) {
dbg.out(output);
}
} catch (IOException e) {
dbg.err("can not successfully native debugger command");
}
}
/** Visit a HelpCommand = (no children). */
public final void visitHelpCommand(final GNode n) {
dbg.help();
dbg.ensurePureContext();
}
/** Visit a ExitCommand = (no children). */
public final void visitExitCommand(final GNode n) {
switch(dbg.getDebugControlStatus()) {
case NONE:
dbg.out("can not process user exit command\n");
break;
case JDB:
case GDB:
dbg.exit();
dbg.enqueEvent(new SessionFinishRequestEvent("user request"));
break;
case JDB_IN_GDB:
case GDB_IN_JDB:
dbg.out("can not process use exit in the nested debug context!\n");
break;
}
}
/** Visit a BreakClassCommand = QualifiedIdentifier LineNumber. */
public final void visitBreakClassCommand(final GNode n) {
String className = n.getNode(0).getString(0);
int lineNumber = Integer.parseInt( n.getString(1));
breakpointManager.setJavaBreakPoint(className, lineNumber);
dbg.ensurePureContext();
}
/** Visit a BreakClassCommand = QualifiedIdentifier. */
public final void visitBreakClassMethodCommand(final GNode n) {
String classAndMethod= Utilities.qualifiedIdentifierToString(n.getGeneric(0));
Pattern p = Pattern.compile("^(.+)\\.([^\\.]+)$");
Matcher m = p.matcher(classAndMethod);
if (m.matches()) {
String cname = m.group(1);
String mname = m.group(2);
breakpointManager.setJavaBreakPoint(cname, mname);
dbg.ensurePureContext();
} else {
dbg.err("can not recognize as <class>.<method>\n");
}
}
/** Visit a BreakFileCommand = FileName LineNumber. */
public final void visitBreakFileCommand(final GNode n) {
String fileName = n.getString(0);
int lineNumber = Integer.parseInt(n.getString(1));
try {
breakpointManager.setNativeBreakpoint(fileName, lineNumber);
} catch(IOException e) {
dbg.err("Can not set breakpoint.");
}
dbg.ensurePureContext();
}
/** Visit a BreakFunctionCommand = Identifier. */
public final void visitBreakFunctionCommand(final GNode n) {
String identifier = n.getString(0);
try {
breakpointManager.setNativeBreakpoint(identifier);
} catch(IOException e) {
dbg.err("Can not set breakpoint.");
}
dbg.ensurePureContext();
}
/** Visit a InfoBreakCommand = (no children). */
public final void visitInfoBreakCommand(final GNode n) {
breakpointManager.showUserBreakPointList();
dbg.ensurePureContext();
}
/** Visit a InfoWatchCommand = (no children). */
public final void visitInfoWatchCommand(final GNode n) {
assert false : "not yet implemented";
}
/** Visit a DeleteCommand = IntegerLiteral. */
public final void visitDeleteCommand(final GNode n) {
int id = Integer.parseInt(n.getNode(0).getString(0));
breakpointManager.clearBreakpoint(id);
dbg.ensurePureContext();
}
/** Visit a NextCommand = (no children). */
public final void visitRunCommand(final GNode n) {
dbg.run();
}
/** Visit a StepCommand = (no children). */
public final void visitStepCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
try {
clearDebuggerContext();
dbg.step();
SourceFileAndLine loc = dbg.getCurrentSourceLevelLocation();
String line = dbg.getCurrentSourceLine();
assert loc != null : "step must be completed at the source line";
dbg.out("Step completed: " + loc + "\n" + line + "\n");
} catch(IOException e) {
dbg.err("could not sucessfully perform stepping\n");
}
}
/** Visit a NextCommand = (no children). */
public final void visitNextCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
try {
clearDebuggerContext();
Event e = dbg.next();
if (e instanceof NativeStepCompletionEvent
|| e instanceof JavaStepCompletionEvent
|| e instanceof Java2NativeReturnEvent
|| e instanceof Native2JavaReturnEvent) {
SourceFileAndLine loc = dbg.getCurrentSourceLevelLocation();
String line = dbg.getCurrentSourceLine();
assert loc != null : "step must be completed at the source line";
dbg.out("Step completed: " + loc + "\n" + line + "\n");
} else if ( e instanceof NativeBreakPointHitEvent) {
EventLoop.reportEvent(dbg, (NativeBreakPointHitEvent)e);
} else if ( e instanceof JavaBreakPointHitEvent) {
EventLoop.reportEvent(dbg, (JavaBreakPointHitEvent)e);
} else {
assert false: "unrecognized event during stepping :" + e;
}
} catch (IOException e) {
dbg.err("could not correctly perform the next command.\n");
}
}
/** Visit a ContinueCommand = (no children). */
public final void visitContinueCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
clearDebuggerContext();
dbg.cont();
}
/** Visit a LocalsCommand = (no children). */
public final void visitLocalsCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
try {
ensureDebuggerContext();
debuggerContext.showLocals(dbg, jeannieSymbolRemapper);
} catch(IOException e) {
dbg.err("could not perform list command.\n");
} finally {
dbg.ensurePureContext();
}
}
/** Visit a ListCommand = (no children). */
public final void visitListCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
try {
ensureDebuggerContext();
debuggerContext.showSourceCode(dbg);
} catch(IOException e) {
dbg.err("could not perform the list command.\n");
} finally {
dbg.ensurePureContext();
}
}
/** Visit a WhereCommand = (no children). */
public final void visitWhereCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
try {
ensureDebuggerContext();
debuggerContext.showWhere(dbg);
} catch(IOException e) {
dbg.err("could not perform where command.\n");
} finally {
dbg.ensurePureContext();
}
}
/** Visit an UpCommand = IntegerLiteral. */
public final void visitUpCommand(final GNode n) {
int steps = Integer.parseInt( n.getNode(0).getString(0));
try {
ensureDebuggerContext();
debuggerContext.unWindStack(dbg, steps);
} catch (IOException e) {
dbg.err("could not perform up\n");
} finally {
dbg.ensurePureContext();
}
}
/** Visit a DownCommand = IntegerLiteral. */
public final void visitDownCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
int steps = Integer.parseInt( n.getNode(0).getString(0));
try {
ensureDebuggerContext();
debuggerContext.windStack(dbg, steps);
} catch (IOException e) {
dbg.err("could not perform up\n");
} finally {
dbg.ensurePureContext();
}
}
/** Visit a StatCommand = (no children). */
public final void visitStatCommand(final GNode n) {
dbg.out("control: " + dbg.getDebugControlStatus() + "\n");
}
/** Visit a WatchCExpressionCommand = C.Expression. */
public final void visitWatchCExpressionCommand(final GNode n) {
assert false : "not yet implemented";
}
/** Visit a WatchJavaFieldCommand = [WatchKind] QualifiedIdentifier. */
public final void visitWatchJavaFieldCommand(final GNode n) {
assert false : "not yet implemented";
}
/** Visit a PrintCExpressionCommand = C.Expression. */
public final void visitPrintCExpressionCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
try {
breakpointManager.freezeActiveBreakPoints();
// Run the evaluation and get a gdb variable holding the result.
Object obj = dispatch(n.getNode(0));
dbg.out("====> " + cprint((CExpr)obj) + "\n");
} catch(IOException e) {
dbg.err("could not correctly print the final result\n");
} finally {
breakpointManager.unfreezeAllBreakpoints();
cleanVJandVC();
dbg.ensurePureContext();
}
}
/** Visit a PrintJavaExpressionCommand = Java.Expression. */
public final void visitPrintJavaExpressionCommand(final GNode n) {
if (!dbg.IsNativeDebuggerAttached()) {
dbg.err("Blink is not initialized to run this command\n");
return;
}
try {
breakpointManager.freezeActiveBreakPoints();
//Run the evaluation and get a jdb variable holding the result.
Object obj = dispatch(n.getNode(0));
if (obj == null) {return;}
dbg.out("=====> " + jprint((JExpr)obj)+ "\n");
} catch(IOException e) {
dbg.err("could not correctly print the final result\n");
} finally {
breakpointManager.unfreezeAllBreakpoints();
cleanVJandVC();
dbg.ensurePureContext();
}
}
/** Visit a JavaInCExpression = Java.UnaryExpression. */
public final CExpr visitJavaInCExpression(final GNode n) throws IOException {
return CV_assign_JExpr((JExpr)dispatch(n.getNode(0)));
}
/** Visit a CInJavaExpression = C.UnaryExpression. */
public final JExpr visitCInJavaExpression(final GNode n)throws IOException {
return JV_assign_CExpr((CExpr)dispatch(n.getNode(0)));
}
/** Visit an IntegerLiteral = String. */
public final JExpr visitIntegerLiteral(final GNode n) {
assert getLanguage(n) == Language.JAVA;
return new JExpr(n.getString(0));
}
/** Visit an IntegerConstant = String. */
public final CExpr visitIntegerConstant(final GNode n) {
assert getLanguage(n) == Language.C;
return new CExpr(p(n.getString(0)));
}
/** Visit an StringConstant = String. */
public final JExpr visitStringConstant(final GNode n) {
assert getLanguage(n) == Language.JAVA;
return new JExpr(q(n.getString(0)));
}
/** Visit an StringLiteral = String. */
public final JExpr visitStringLiteral(final GNode n) {
assert getLanguage(n) == Language.JAVA;
return new JExpr(q(n.getString(0)));
}
/** Visit an NullLiteral = (no children). */
public final JExpr visitNullLiteral(final GNode n) {
assert getLanguage(n) == Language.JAVA;
return new JExpr(p("null"));
}
/** Visit a MetaVariable = Identifier. */
public final BExpr visitMetaVariable(final GNode n) throws IOException {
assert false : "not implemented yet";
return null;
}
/** Visit a PrimaryIdentifier = String. */
public BExpr visitPrimaryIdentifier(final GNode n) throws IOException {
String idName = (String)n.get(0);
//try id remapping for the Jeannie frame.
ensureDebuggerContext();
SourceFileAndLine loc = debuggerContext.getCurrentLocation();
VariableRemapEntry idRemap = jeannieSymbolRemapper.lookUpVariableRemap(idName, loc
.getSourceFile(), loc.getSourceLine());
String idExpr = null;
if (idRemap != null) {
idExpr = idRemap.targetLanguageExpression();
}
if (idExpr == null) {
idExpr = idName;
}
switch(getLanguage(n)) {
case C: return eval(n, new CExpr(idExpr));
case JAVA: return eval(n, new JExpr(idExpr));
}
assert false : "not reachable";
return null;
}
/** Visit a ThisExpression = [Expression]. */
public final void visitThisExpression(final GNode n) {
assert getLanguage(n) == Language.JAVA;
assert false : "The Blink debugger does not support" +
"qualified this in Java expression.";
}
/** Visit an AdditiveExpression = Expression ("+" / "-") Expression. */
public final BExpr visitAdditiveExpression(final GNode n) throws IOException {
switch(getLanguage(n)) {
case C: return eval(n, CExpr.binop(
(CExpr)dispatch(n.getNode(0)),
n.getString(1),
(CExpr)dispatch(n.getNode(2))));
case JAVA: return eval(n, JExpr.binop(
(JExpr)dispatch(n.getNode(0)),
(String)(n.get(1)),
(JExpr)dispatch(n.getNode(2))
));
}
assert false : "not reachable";
return null;
}
/** Visit a EqualityExpression = Expression ("==" / "!=") Expression. */
public final BExpr visitEqualityExpression(final GNode n) throws IOException {
assert !isLValue(n) : "the binary operation never generages l-value.";
switch(getLanguage(n)) {
case C: return eval(n, CExpr.binop(
(CExpr)dispatch(n.getNode(0)),
n.getString(1),
(CExpr)dispatch(n.getNode(2)))
);
case JAVA: return eval(n, JExpr.binop(
(JExpr)dispatch(n.getNode(0)),
n.getString(1),
(JExpr)dispatch(n.getNode(2))
));
}
assert false : "not reachable";
return null;
}
/** Visit a FunctionCall = Expression [ExpressionList]. */
public final CExpr visitFunctionCall(final GNode n) throws IOException {
assert getLanguage(n) == Language.C;
assert !isLValue(n) : "function call can never be l-value.";
return eval(n, CExpr.funcCall(
(CExpr)dispatch(n.getNode(0)),
(CExpr[])dispatch(n.getNode(1))));
}
/** Visit ExpressionList = Expression+. */
public final CExpr[] visitExpressionList(final GNode n) {
assert getLanguage(n) == Language.C;
assert !isLValue(n) : "function arguments can never be l-value.";
CExpr[] exprs= new CExpr[n.size()];
for (int i=0; i<n.size(); i++) {
exprs[i] = (CExpr)dispatch(n.getNode(i));
}
return exprs;
}
/** Visit a CallExpression = [Expression] null MethodName Arguments. */
public JExpr visitCallExpression(final GNode n) throws IOException {
assert getLanguage(n) == Language.JAVA;
assert !isLValue(n) : "method call never gernates l-value.";
return eval(n, JExpr.methodCall(
(JExpr) dispatch(n.getNode(0)),
n.getString(2),
(JExpr[]) dispatch(n.getNode(3))
));
}
/** Visit Arguments = Expression*. */
public final JExpr[] visitArguments(final GNode n) {
assert getLanguage(n) == Language.JAVA;
JExpr[] args = new JExpr[n.size()];
for (int i=0; i<n.size(); i++)
args[i] = (JExpr)dispatch(n.getNode(i));
return args;
}
/** Visit an AddressExpression = Expression. */
public final CExpr visitAddressExpression(final GNode n) {
assert getLanguage(n) == Language.C;
assert !isLValue(n) : "& never gernates l-value.";
return CExpr.address((CExpr)dispatch(n.getNode(0)));
}
/** Visit a Expression = Expression ("=" / "+=" / "-=" / "*=" / "/=" / "&=" / "|=" / "^=" / "%=" / "<<=" / ">>=" / ">>>=") Expression. */
public final JExpr visitExpression(final GNode n) throws IOException {
assert getLanguage(n) == Language.JAVA;
assert !isLValue(n) : "the assignment never gernates l-value.";
// mark the LHS is the l-value expression generator.
setLValue((GNode)n.getNode(0));
return eval(n, JExpr.binop(
(JExpr)dispatch(n.getNode(0)),
n.getString(1),
(JExpr)dispatch(n.getNode(2))
));
}
/** Visit an AssignmentExpression = Expression ("=" / "+=" / "-=" / "*=" / "/=" / "%=" / "<<=" / ">>=" / "&=" / "^=" / "|=") Expression. */
public final CExpr visitAssignmentExpression(final GNode n) throws IOException {
assert getLanguage(n) == Language.C;
assert !isLValue(n) : "the assignment never gernates l-value.";
// mark the LHS is the l-value expression generator.
setLValue((GNode)n.getNode(0));
return eval(n, CExpr.binop(
(CExpr)dispatch(n.getNode(0)),
n.getString(1),
(CExpr)dispatch(n.getNode(2))
));
}
/** Visit an IndirectionExpression = Expression. */
public final CExpr visitIndirectionExpression(final GNode n) throws IOException {
assert getLanguage(n) == Language.C;
return eval(n, CExpr.indirect((CExpr)dispatch(n.getNode(0))));
}
/** Visit a QualifiedIdentifier = Identifier+. */
public final JExpr visitQualifiedIdentifier(final GNode n) throws IOException {
assert getLanguage(n) == Language.JAVA;
return eval(n, new JExpr(Utilities.qualifiedIdentifierToString(n)));
}
/** Visit a SelectionExpression = Expression Identifier. */
public final JExpr visitSelectionExpression(final GNode n) throws IOException {
assert getLanguage(n) == Language.JAVA;
return eval(n, JExpr.field((JExpr)dispatch(n.getNode(0)),n.getString(1)));
}
/** Visit a DirectComponentSelection = Expression String. */
public final CExpr visitDirectComponentSelection(final GNode n) throws IOException {
assert getLanguage(n) == Language.C;
return eval(n, CExpr.fieldDirect((CExpr)dispatch(n.getNode(0)), n.getString(1)));
}
/** Visit a SubscriptExpression = Expression Expression. */
public final BExpr visitSubscriptExpression(final GNode n) throws IOException {
switch(getLanguage(n)) {
case JAVA:
return eval(n, JExpr.arraySelect(
(JExpr)dispatch(n.getNode(0)),
(JExpr)dispatch(n.getNode(1))));
case C:
return eval(n, CExpr.arraySelect(
(CExpr)dispatch(n.getNode(0)),
(CExpr)dispatch(n.getNode(1))));
}
assert false : "not reachable";
return null;
}
/**
* jprint <JExpr>
*
* This is one of the six Blink expression evaluation primitives.
* @param jexpr The expression.
* @return The result of evluating the <JExpr>
*/
private String jprint(JExpr jexpr) throws IOException {
dbg.ensureJDBContext();
DebuggerContext c = ensureDebuggerContext();
JavaCallFrame jf = c.getCurrentJavaFrame();
String result = dbg.jdb.print(jf, jexpr.getValueExpr());
if (dbg.options.getVerboseExprEvaluation() >= 1) {
dbg.out("\tjprint " + jexpr.getValueExpr() + "\n");
}
return result;
}
/**
* cprint <CExpr>
*
* This is one of the six Blink expression evaluation primitives.
* @param cexpr The expression.
* @return The result of evaluating the <CExpr>
*/
private String cprint(CExpr cexpr) throws IOException {
// talk to the GDB to print the final result.
DebuggerContext c = ensureDebuggerContext();
NativeCallFrame nframe = c.getCurrentNativeFrame();
dbg.ensureGDBContext();
String result = dbg.ndb.eval(nframe, cexpr.getValueExpr());
if (dbg.options.getVerboseExprEvaluation() >= 1) {
dbg.out("\tcprint " + cexpr.getValueExpr() + "\n");
}
return result;
}
/**
* <JV> := <JExpr>
*
* This is one of the six Blink expression evaluation primitives.
*
* @param jexpr The Java expression.
* @return The JV variable.
*/
private JV JV_assign_JExpr(JExpr jexpr) throws IOException {
// get the java expression for the new VJ.
DebuggerContext c = ensureDebuggerContext();
JavaCallFrame jf = c.getCurrentJavaFrame();
dbg.ensureJDBContext();
String vjid = dbg.jdb.newConvenienceVariable(jf, jexpr.getValueExpr());
String vjexpr = dbg.jdb.getConvenienceVariableRValueExpression(vjid);
String javaType = dbg.jdb.getConvenienceVariableJavaType(vjid);
JV jv = new JV( "vj" + vjid, javaType, vjexpr);
if (dbg.options.getVerboseExprEvaluation() >= 1) {
dbg.out("\t" + jv.getVjID() + "(" + jv.getType() + ") := "
+ jexpr.getValueExpr() + "\n");
}
return jv;
}
/**
* <JV> := <CExpr>.
*
* This is one of the six Blink expression evaluation primitives.
* @param cexpr The C expression.
* @return The jv variable.
*/
private JV JV_assign_CExpr(CExpr cexpr) throws IOException {
//get the expected type of the RHS.
DebuggerContext c = ensureDebuggerContext();
NativeCallFrame nframe = c.getCurrentNativeFrame();
dbg.ensureGDBContext();
String eType = dbg.ndb.whatis(nframe,cexpr.getValueExpr());
//choose the right c2j value conversion debug agent function.
String dbaExprFunc;
if (eType.equals("int")) {
dbaExprFunc = "bda_set_vj_from_cexpr_jint";
} else if (eType.equals("long")) {
dbaExprFunc = "bda_set_vj_from_cexpr_jlong";
} else if (eType.equals("float")) {
dbaExprFunc = "bda_set_vj_from_cexpr_jfloat";
} else if (eType.equals("double")) {
dbaExprFunc = "bda_set_vj_from_cexpr_jdouble";
} else if (eType.equals("jobject")) {
dbaExprFunc = "bda_set_vj_from_cexpr_jobject";
} else {
dbaExprFunc = "bda_set_vj_from_cexpr_jobject";
}
//evaluate the RHS and convert the result into the VJ in the LHS.
String jnienv = dbg.ensureJNIENV();
String vjid = dbg.ndb.eval(nframe,
dbaExprFunc + "(" + jnienv + "," + cexpr.getValueExpr() + ")");
// get the java expression for the new VJ.
dbg.ensureJDBContext();
String vjexpr = dbg.jdb.getConvenienceVariableRValueExpression(vjid);
String javaType = dbg.jdb.getConvenienceVariableJavaType(vjid);
JV jv = new JV( "vj" + vjid, javaType, vjexpr);
if (dbg.options.getVerboseExprEvaluation() >= 1) {
dbg.out("\t" + jv.getVjID() + "(" + jv.getType() + ") := "
+ cexpr.getValueExpr() + "\n");
}
return jv;
}
/**
* <VC> : = <JExpr>
*
* This is one of the six Blink expression evaluation primitives.
* @param jexpr The Java expression.
* @return The vc variable.
*/
private CV CV_assign_JExpr(JExpr jexpr) throws IOException {
//ensure jdb access.
dbg.ensureJDBContext();
DebuggerContext c = ensureDebuggerContext();
JavaCallFrame jframe = c.getCurrentJavaFrame();
// ask the jdb to evalute the RHS, and store the result into the Blink agent
// variable table. For the type of the RHS, we rely on the JDB's method
// overloading. Like the GDB, the JDB does not tell us an static expression
// type without actually running the expression.
String vjid = dbg.jdb.newConvenienceVariable(jframe, jexpr.getValueExpr());
// Now ask the debug agent to tell us the JNI-equivalent type for the CV.
String jniType = dbg.jdb.getConvenienceVariableJNIType(vjid);;
// Now, let the gdb to obtain the new CV value.
NativeCallFrame nframe = c.getCurrentNativeFrame();
dbg.ensureGDBContext();
String jnienv = dbg.ensureJNIENV();
String vcid = getNewCTmpVarIdentifier();
dbg.ndb.setVariable(nframe,
vcid,
"bda_get_cvalue_from_vj_" + jniType + "("+ jnienv + "," + vjid + ")");
CV cv = new CV(vcid, jniType);
if (dbg.options.getVerboseExprEvaluation() >= 1) {
dbg.out("\t" + cv.getVcId() + "(" + cv.getType() + ") := "
+ jexpr.getValueExpr() + "\n");
}
return cv;
}
/**
* Implements <VC> : = <CExpr>.
*
* @param cexpr The C expression
* @return The cv variable.
*/
private CV CV_assign_CExpr(CExpr cexpr) throws IOException {
// Now, let the gdb to obtain the new CV value.
String vcid = getNewCTmpVarIdentifier();
DebuggerContext c = ensureDebuggerContext();
NativeCallFrame nframe = c.getCurrentNativeFrame();
dbg.ensureGDBContext();
dbg.ndb.setVariable(nframe, vcid, cexpr.getValueExpr());
//get the expected type of the RHS.
String ctype = dbg.ndb.whatis(nframe, cexpr.getValueExpr());
CV cv = new CV(vcid, ctype);
if (dbg.options.getVerboseExprEvaluation() >= 1) {
dbg.out("\t" + cv.getVcId() + "(" + cv.getType() + ") := "
+ cexpr.getValueExpr() + "\n");
}
return cv;
}
/** Finish the expression evaluation */
private final void cleanVJandVC() {
//handle jdb part.
try {
dbg.ensureJDBContext();
dbg.jdb.resetConvenienceVariables();
} catch (IOException e) {
dbg.err("could not successfully clean JDB temp varaibles.\n");
}
//handle gdb part.
nextVCIdentifier = 0;
}
/** Ensure the debugger context is available at the return of this method. */
private final DebuggerContext ensureDebuggerContext() throws IOException {
if (debuggerContext != null ) {
return debuggerContext;
}
// obtain the current debugger context.
dbg.ensurePureContext();
CallStack callStack = CallStack.extractCallStack(dbg,
jeannieSymbolRemapper);
debuggerContext = new DebuggerContext(callStack);
return debuggerContext;
}
/** Clear the current debugger context. */
private final void clearDebuggerContext() {
debuggerContext = null;
}
/** Getter the langg property of the node. */
private static Language getLanguage(final GNode n) {
return (Language) n.getProperty("language");
}
/** Setter of the l-value marker of the node. */
private static void setLValue(final GNode n) {
n.setProperty("blink.lvalue", new Boolean("true"));
}
/** Getter of the l-value marker of the node. */
private boolean isLValue(final GNode n) {
Object o = n.getProperty("blink.lvalue");
if (o instanceof Boolean ) {
Boolean b = (Boolean)o;
return b.booleanValue();
} else {
return false;
}
}
/** The next CV unique identifier number. */
private int nextVCIdentifier = 0;
/** Allocate a new gdb meta variable name. */
private String getNewCTmpVarIdentifier() {
return "$vc" + nextVCIdentifier++;
}
/** do l-value sensitive indirection. */
private CExpr eval(final GNode n, final CExpr e) throws IOException {
if (isLValue(n)) {
return e;
} else {
return CV_assign_CExpr(e);
}
}
/** do l-value sensitive indirection. */
private JExpr eval(final GNode n, final JExpr e) throws IOException {
if (isLValue(n)) {
return e;
} else {
return JV_assign_JExpr(e);
}
}
/** Qoute a string. */
private static String q(String s) {
return "\"" + s + "\"";
}
/** Surround the straing with paranthesis. */
private static String p(String s) {
return "(" + s + ")";
}
/** join a number of expressions by inserting a string between them. */
private static String join(String s, BExpr[] exprs) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < exprs.length;i++) {
if (i == 0) {
sb.append(exprs[i].getValueExpr());
} else {
sb.append(s).append(exprs[i].getValueExpr());
}
}
return sb.toString();
}
/**
* The Blink expression.
*/
private static abstract class BExpr {
final String val;
BExpr(String val) {
this.val = val;
}
/** The getter method for val. */
String getValueExpr() {
return val;
}
/** The string representation. */
public String toString() {
return getValueExpr();
}
}
/** The Java expression. */
private static class JExpr extends BExpr {
/** The constructor. */
JExpr(String val) {
super(val);
}
/** a number of utility methods. */
static JExpr field(JExpr expr, String s) {
return new JExpr("(" + expr.getValueExpr() + "." + s + ")");
}
static JExpr binop(JExpr expr0, String operator, JExpr expr1) {
return new JExpr("(" + expr0.getValueExpr() + operator + expr1.getValueExpr() + ")");
}
static JExpr methodCall(JExpr expr0, String meth, JExpr[] args ) {
return new JExpr(
"(" + expr0.getValueExpr() + "." + meth + "(" + join(",", args) + "))"
);
}
static JExpr arraySelect(JExpr exprArray, JExpr exprIndex) {
return new JExpr(
"(" + exprArray.getValueExpr() + "[" + exprIndex.getValueExpr() + "])"
);
}
}
/** The C Expression. */
private static class CExpr extends BExpr {
/** The constructor. */
CExpr(String val) {
super(val);
}
/** a number of utility methods. */
static CExpr address(CExpr expr) {
return new CExpr("(&"+ expr.getValueExpr() + ")");
}
static CExpr indirect(CExpr expr) {
return new CExpr("(*" + expr.getValueExpr() + ")");
}
static CExpr binop(CExpr expr0, String operator, CExpr expr1) {
return new CExpr("(" + expr0.getValueExpr() + operator + expr1.getValueExpr() + ")");
}
static CExpr fieldDirect(CExpr expr, String comp) {
return new CExpr("(" + expr.getValueExpr() + "." + comp + ")");
}
static CExpr funcCall(CExpr exprFunc, CExpr[] args ) {
return new CExpr(exprFunc.getValueExpr() + "(" + join(",", args) + ")");
}
static CExpr arraySelect(CExpr exprArray, CExpr exprIndex) {
return new CExpr(
"(" + exprArray.getValueExpr() + "[" + exprIndex.getValueExpr() + "])"
);
}
}
/**
* The Java psuduo variable inside the Blink.
*/
private static class JV extends JExpr {
private final String vjId;
private final String type;
/** Constructors. */
JV(String vjId, String type, String expr) {
super(expr);
this.type = type;
this.vjId = vjId;
}
/** Getter methods. */
public String getVjID() {return vjId;}
public String getType() {return type;}
}
/**
* The C pseudo variable inside the Blink.
*/
private static class CV extends CExpr {
private final String vcId;
private final String type;
/** Constructors. */
CV(String vcId, String type) {
super("(" + vcId + ")");
this.vcId = vcId;
this.type = type;
}
/** Getter methods. */
public String getType() {return type;}
public String getVcId() {return vcId;}
}
}