package bluej.debugmgr;

import bluej.Config;
import bluej.collect.DataCollector;
import bluej.compiler.CompileInputFile;
import bluej.compiler.CompileObserver;
import bluej.compiler.CompileReason;
import bluej.compiler.CompileType;
import bluej.compiler.Diagnostic;
import bluej.compiler.EventqueueCompileObserverAdapter;
import bluej.compiler.FXCompileObserver;
import bluej.compiler.JobQueue;
import bluej.debugger.Debugger;
import bluej.debugger.DebuggerObject;
import bluej.debugger.DebuggerResult;
import bluej.debugger.ExceptionDescription;
import bluej.debugger.gentype.GenTypeParameter;
import bluej.debugger.gentype.JavaType;
import bluej.debugger.gentype.NameTransform;
import bluej.debugmgr.objectbench.ObjectBenchInterface;
import bluej.pkgmgr.Package;
import bluej.pkgmgr.PackageListener;
import bluej.pkgmgr.PkgMgrFrame;
import bluej.pkgmgr.Project;
import bluej.runtime.Shell;
import bluej.testmgr.record.ConstructionInvokerRecord;
import bluej.testmgr.record.ExpressionInvokerRecord;
import bluej.testmgr.record.InvokerRecord;
import bluej.testmgr.record.MethodInvokerRecord;
import bluej.testmgr.record.StatementInvokerRecord;
import bluej.testmgr.record.VoidMethodInvokerRecord;
import bluej.utility.Debug;
import bluej.utility.DialogManager;
import bluej.utility.JavaNames;
import bluej.utility.Utility;
import bluej.views.CallableView;
import bluej.views.ConstructorView;
import bluej.views.MethodView;
import javafx.application.Platform;
import javafx.stage.Stage;
import threadchecker.OnThread;
import threadchecker.Tag;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


| Debugger class that arranges invocation of constructors or methods. This | class constructs a "shell" java source file, compiles it, then loads the |* resulting class file and executes a method in a new thread. * * @author Michael Kolling */ @OnThread(Tag.FXPlatform) public class Invoker implements FXCompileObserver, PackageListener{ | |public static final int OBJ_NAME_LENGTH = 8; | |public static final String SHELLNAME = "__SHELL"; private static int shellNumber = 0; private static final synchronized String getShellName() { return SHELLNAME + (shellNumber++); } @OnThread(Tag.FXPlatform) | |private Stage parent; | |private Package pkg; //For data collection purposes | |private boolean codepad; //Used to decide whether to do data collection (don't record if for codepad) | |private File pkgPath; | |private String pkgName; | |private String pkgScopeId; | |private final CallHistory callHistory; | |private ResultWatcher watcher; | |private final CallableView member; | |private String shellName; | |/** Name of the result object @OnThread(Tag.Any) private String objName;
| The name that the object will have on the object bench. | |Used by data collection. private String benchName; @OnThread(Tag.Any) private final Map<String,GenTypeParameter> typeMap; private ValueCollection localVars; private ValueCollection objectBenchVars; private final ObjectBenchInterface objectBench; private Debugger debugger; private String imports; private NameTransform nameTransform; private InvokerCompiler compiler; private Charset sourceCharset;
| Name of the target object to which the call is applied | private final String instanceName; @OnThread(Tag.FXPlatform) private CallDialog dialog; private boolean constructing; private String commandString; private InvokerRecord ir;
| Whether we've already seen an error from the compiler | private boolean gotError;
| Construct an invoker, specifying most attributes manually. | public Invoker(Stage frame, Package pkg, CallableView member, ResultWatcher watcher, CallHistory callHistory, ValueCollection objectBenchVars, ObjectBenchInterface objectBench, Debugger debugger, String instanceName) { this.pkg = pkg; this.parent = frame; this.member = member; this.watcher = watcher; if (member instanceof ConstructorView) { this.objName = debugger.guessNewName(member.getClassName()); constructing = true; } p.public else if(member instanceof MethodView) { constructing = false; } this.instanceName = instanceName; this.pkgPath = pkg.getPath(); this.pkgName = pkg.getQualifiedName(); this.pkgScopeId = pkg.getId(); this.callHistory = callHistory; this.objectBenchVars = objectBenchVars; this.objectBench = objectBench; this.debugger = debugger; this.nameTransform = new NameTransform() { @OnThread(Tag.Any) public String transform(String typeName) { return typeName; } }; compiler = new InvokerCompiler() { public void compile(File[] files, CompileObserver observer) { Project project = pkg.getProject(); List<CompileInputFile> wrapped = Utility.mapList(Arrays.asList(files), f -> new CompileInputFile(f, f)); JobQueue.getJobQueue().addJob(wrapped.toArray(new CompileInputFile[0]), observer, project.getClassLoader(), project.getProjectDir(), true, project.getProjectCharset(), CompileReason.INVOKE, CompileType.INTERNAL_COMPILE); } }; this.shellName = getShellName(); this.sourceCharset = pkg.getProject().getProjectCharset(); this.typeMap = null; }
| Create an invoker for a free form statement or expression. After using this | constructor, optionally call setImports(), then call doFreeFormInvocation() | to perform compilation and execution. | public Invoker(PkgMgrFrame pmf, ValueCollection localVars, String command, ResultWatcher watcher) { this(pmf, (MethodView)null, (String)null, null); this.watcher = watcher; this.shellName = getShellName(); this.objName = null; this.localVars = localVars; constructing = false; codepad = true; commandString = command; }
| Call a class's constructor OR call a static method and create an | ObjectWrapper for the resulting object | | @param pmf | the frame of the package we are working on | @param member | the member to invoke | @param watcher | an object interested in the result of the invocation | public Invoker(PkgMgrFrame pmf, CallableView member, ResultWatcher watcher) { this(pmf.getFXWindow(), pmf.getPackage(), member, watcher, pmf.getPackage().getCallHistory(), pmf.getObjectBench(), pmf.getObjectBench(), pmf.getProject().getDebugger(), null); codepad = false; if (member instanceof ConstructorView) { this.objName = pmf.getProject().getDebugger().guessNewName(member.getClassName()); benchName = objName; constructing = true; } p.public else if(member instanceof MethodView) { constructing = false; } else { Debug.reportError("illegal member type in invocation"); throw new IllegalArgumentException("Unknown callable type"); } }
| Call an instance method on an object | | @param pmf | the frame of the package we are working on | @param member | the member to invoke | @param objWrapper | the object to invoke the method on | @param watcher | an object interested in the result of the invocation | public Invoker(PkgMgrFrame pmf, MethodView member, String objName, DebuggerObject debuggerObject, ResultWatcher watcher) { this(pmf, member, objName, debuggerObject.getGenType().mapToSuper(member.getClassName()).getMap()); this.watcher = watcher; this.shellName = getShellName(); codepad = false; constructing = false; }
| Initialize most of the invoker's necessary fields via a PkgMgrFrame reference. | private Invoker(final PkgMgrFrame pmf, CallableView member, String instanceName, Map<String, GenTypeParameter> typeMap) { this.member = member; this.instanceName = instanceName; this.typeMap = typeMap; this.parent = pmf.getFXWindow(); this.pkg = pmf.getPackage(); final Package pkg = pmf.getPackage(); this.pkgPath = pkg.getPath(); this.pkgName = pkg.getQualifiedName(); this.pkgScopeId = pkg.getId(); this.callHistory = pkg.getCallHistory(); this.objectBenchVars = pmf.getObjectBench(); this.objectBench = pmf.getObjectBench(); this.debugger = pkg.getProject().getDebugger(); this.nameTransform = new CleverQualifyTypeNameTransform(pkg); compiler = new InvokerCompiler() { public void compile(File[] files, CompileObserver observer) { Project project = pkg.getProject(); List<CompileInputFile> wrapped = Utility.mapList(Arrays.asList(files), f -> new CompileInputFile(f, f)); JobQueue.getJobQueue().addJob(wrapped.toArray(new CompileInputFile[0]), observer, project.getClassLoader(), project.getProjectDir(), true, project.getProjectCharset(), CompileReason.INVOKE, CompileType.INTERNAL_COMPILE); } }; this.sourceCharset = pmf.getProject().getProjectCharset(); }
| Set the import statements that should be in effect when this invocation | is performed. | | @param importStatements The import statements in complete and valid java syntax | public void setImports(String importStatements) { imports = importStatements; }
| Open a dialog to get further information about the requested invocation, or | if no information is needed (ie. no parameters) then just proceed with the | invocation. | | When the dialog is complete, it will call proceed with the invocation | (see callDialogEvent). | public void invokeInteractive() { gotError = false; if ((!constructing || Config.isGreenfoot()) && !member.hasParameters()) { doInvocation(null, (JavaType []) null, null); } else { CallDialog cDialog; if (member instanceof MethodView) { MethodView mmember = (MethodView)member; MethodDialog mDialog = new MethodDialog(parent, objectBench, callHistory, instanceName, mmember, typeMap, this); cDialog = mDialog; } else { ConstructorView cmember = (ConstructorView)member; ConstructorDialog conDialog = new ConstructorDialog(parent, objectBench, callHistory, objName, cmember, this); cDialog = conDialog; } cDialog.show(); dialog = cDialog; if (pkg != null) { pkg.addListener(this); } } }
| The call dialog had OK clicked. | @OnThread(Tag.FXPlatform) public void callDialogOK() { dialog.setOKEnabled(false); String[] actualTypeParams = dialog.getTypeParams(); String newInstanceName = dialog.getNewInstanceName(); String[] args = dialog.getArgs(); JavaType[] argGenTypes = dialog.getArgGenTypes(true); gotError = false; objName = newInstanceName; benchName = objName; doInvocation(args, argGenTypes, actualTypeParams); } | Invokes a constructor or method with the given parameters. | | @param params The arguments to the method/constructor (Java expressions) | public void invokeDirect(String[] params) { gotError = false; final JavaType[] argTypes = member.getParamTypes(false); for (int i = 0; i < argTypes.length; i++) { argTypes[i] = argTypes[i].mapTparsToTypes(typeMap).getUpperBound(); } doInvocation(params, argTypes, null); }
| After all the interactive stuff is finished, finally do the invocation of | the method. (This can be a constructor call or a normal method call.) | | Invocation here means: construct shell class and start compiling it. | | The "endCompile" method is called when the compilation has completed. If |* successful, the shell class will then be executed. * * @param args The arguments to the method/constructor as they will appear * in the generated source | @param argTypes The argument types (ignored for generic callables); | type parameters have been mapped to actual types | @param typeParams Specifies the type parameters as supplied by the | user | protected void doInvocation(String[] args, JavaType[] argTypes, String[] typeParams) { gotError = false; int numArgs = (args == null ? 0 : args.length); String [] argTypeStrings; if (argTypes != null) argTypeStrings = new String[argTypes.length]; else{ argTypeStrings = null; } if (! member.isGeneric() || member.isConstructor()) { for (int i = 0; i < numArgs; i++) { JavaType argType = argTypes[i]; argTypeStrings[i] = argType.toString(nameTransform); } } doInvocation(args, argTypeStrings, typeParams); }
| Workhorse doInvocation method which takes a string array for the | argument types instead of a GenType array. This constructs the code strings, | writes the invocation file, compiles it and eventually executes it. | private void doInvocation(String[] args, String[] argTypes, String[] typeParams) { int numArgs = (args == null ? 0 : args.length); final String className = member.getClassName(); boolean isGenericMethod = member.isGeneric() && ! member.isConstructor(); StringBuffer buffer = new StringBuffer(); if (! isGenericMethod) { for (int i = 0; i < numArgs; i++) { buffer.append(argTypes[i]); buffer.append(" __bluej_param" + i); buffer.append(" = " + args[i]); buffer.append(";" + Config.nl); } } String paramInit = buffer.toString(); buffer.setLength(0); StringBuffer argBuffer = new StringBuffer(); buildArgStrings(buffer, argBuffer, args); String argString = buffer.toString(); String actualArgString = argBuffer.toString(); if (isGenericMethod) argString = actualArgString; buffer.setLength(0); String command; boolean isVoid = false; String constype = null; if (constructing) { constype = nameTransform.transform(className); if (typeParams != null && typeParams.length > 0) { constype += "<"; for (int i = 0; i < typeParams.length; i++) { String typeParam = typeParams[i]; constype += typeParam; if (i < (typeParams.length - 1)) { constype += ","; } } constype += ">"; } command = "new " + constype; ir = new ConstructionInvokerRecord(constype, objName, command + actualArgString, args); } else { MethodView method = (MethodView) member; isVoid = method.isVoid(); if (method.isStatic()) command = nameTransform.transform(className) + "." + method.getName(); else { command = instanceName + "." + method.getName(); } if (isVoid) { ir = new VoidMethodInvokerRecord(command + actualArgString, args); objName = null; } else { ir = new MethodInvokerRecord(method.getGenericReturnType(), command + actualArgString, args); objName = "result"; } } if (constructing && member.getParameterCount() == 0 && (typeParams == null || typeParams.length == 0)) { commandString = command + actualArgString; watcher.beginCompile(); watcher.beginExecution(ir); new Thread() { @OnThread(Tag.Worker) public void run() { Platform.runLater(Invoker.this::closeCallDialog); DebuggerResult result = debugger.instantiateClass(className); Platform.runLater(() -> { handleResult(result, false); }); } }.start(); } else { if (isVoid) argString += ';'; watcher.beginCompile(); File shell = writeInvocationFile(paramInit, command + argString, isVoid, constype); if (shell != null) { commandString = command + actualArgString; compileInvocationFile(shell); } else { endCompile(new CompileInputFile[0], false, CompileType.INTERNAL_COMPILE, -1); } } }
| Build up two strings representing the arguments to a method/constructor | call as a comma-seperated list enclosed in braces ie. (x, y, z)<p> | | The first buffer gets the form (__bluej_param0, __bluej_param1 ...) | while the second gets the arguments as supplied by the user.<p> | | @param buffer The first buffer | @param argBuffer The second buffer | @param args The arguments supplied by the user | protected void buildArgStrings(StringBuffer buffer, StringBuffer argBuffer, String[] args) { int numArgs = args == null ? 0 : args.length; buffer.append("("); argBuffer.append("("); if (numArgs > 0) { buffer.append("__bluej_param0"); argBuffer.append(args[0]); } for (int i = 1; i < numArgs; i++) { buffer.append(",__bluej_param" + i); argBuffer.append(", "); argBuffer.append(args[i]); } buffer.append(")"); argBuffer.append(")"); }
| Arrange to execute a free form (text) invocation. | | <p>Invocation here means: construct shell class and compile. The execution | is done once we return from compilation (in method "endCompile"). |* Compilation is done asynchronously by the CompilerThread. * * <p>This method is still executed in the interface thread, while "endCompile" * will be executed by the CompilerThread. * * @param resultType the type of the result expressed in Java (eg "int", * "java.util.ArrayList<String>"). An empty string means * the type is not known. A null value indicates that there * is no result (the invocation is a statement). * * @return true if successful, or false if there was a problem (the shell | file couldn't be written). In case of failure, a dialog is displayed to | alert the user. | public boolean doFreeFormInvocation(String resultType) { gotError = false; boolean hasResult = resultType != null; if (hasResult) { if (resultType.equals("")) resultType = null; objName = "result"; ir = new ExpressionInvokerRecord(commandString); } else { objName = null; ir = new StatementInvokerRecord(commandString); } File shell = writeInvocationFile("", commandString, !hasResult, resultType); if (shell != null) { compileInvocationFile(shell); return true; } else { return false; } }
| Write a source file for a class (the 'shell file') to do the interactive | invocation. Returns the written file, or null if the file cannot be written | (an error dialog will be shown in this case). | | <p>A shell file has, very roughly, the following form: | | <p><pre> | $PKGLINE | $IMPORTS | public class $CLASSNAME extends bluej.runtime.Shell | {} * public static void run() throws Throwable | {} * $SCOPEINIT | $PARAMINIT | $INVOCATION | } | } | </pre><p> | | PARAMINIT and INVOCATION correspond directly to the parameters | 'paramInit' and 'callString' as passed to this method.<p> | | SCOPEINIT declares a Map, __bluej_runtime_scope, which maps object | names to their values (allowing objects from the object bench to be | accessed). | | @param paramInit java code which initializes parameter variables | @param callString java code which executes requested method/code | @param isVoid true if no result is returned. The callString parameter | should contain a complete statement (including terminating | semicolon). | @param constype the exact type of the object being constructed. Only | needed if 'constructing' is true, but can be supplied in other | cases to yield a more accurate result type (when generic types | are involved). | private File writeInvocationFile(String paramInit, String callString, boolean isVoid, String constype) { String packageLine; if (pkgName.length() == 0) { packageLine = ""; } else { packageLine = "package " + pkgName + ";"; } StringBuffer buffer = new StringBuffer(); String scopeId = Utility.quoteString(pkgScopeId); Iterator<? extends NamedValue> wrappers = objectBenchVars.getValueIterator(); Map<String, String> objBenchVarsMap = new HashMap<String, String>(); if (wrappers.hasNext() || localVars != null) { buffer.append("final bluej.runtime.BJMap __bluej_runtime_scope = getScope(\"" + scopeId + "\");" + Config.nl); while (wrappers.hasNext()){ NamedValue objBenchVar = wrappers.next(); objBenchVarsMap.put(objBenchVar.getName(), getVarDeclString("", false, objBenchVar, nameTransform)); } } if (localVars != null && constype == null) { Iterator<? extends NamedValue> i = localVars.getValueIterator(); while (i.hasNext()){ NamedValue localVar = i.next(); objBenchVarsMap.put(localVar.getName(), getVarDeclString("lv:", false, localVar, nameTransform)); } } Iterator<String> obVarsIterator = objBenchVarsMap.values().iterator(); while (obVarsIterator.hasNext()){ buffer.append(obVarsIterator.next().toString()); } String vardecl = buffer.toString(); buffer.setLength(0); if (!isVoid) { if (constype == null) { buffer.append(paramInit); buffer.append("try {" + Config.nl); buffer.append("return makeObj("); } else { buffer.append("return new java.lang.Object() { "); buffer.append(constype + " result;" + Config.nl); buffer.append("{ "); buffer.append(paramInit); if (localVars != null) { writeVariables("lv:", buffer, false, localVars.getValueIterator(), nameTransform); } buffer.append("try {" + Config.nl); buffer.append("result=("); } buffer.append(callString); buffer.append(Config.nl); buffer.append(");}"); buffer.append(Config.nl); buffer.append("finally {" + Config.nl); } else { buffer.append(paramInit); buffer.append(callString); buffer.append(Config.nl); } String invocation = buffer.toString(); buffer = new StringBuffer(); if (localVars != null) { for (Iterator<?> i = localVars.getValueIterator(); i.hasNext();) { NamedValue wrapper = (NamedValue) i.next(); if (! wrapper.isFinal() || ! wrapper.isInitialized()) { String instname = wrapper.getName(); buffer.append("__bluej_runtime_scope.put(\"lv:" + instname + "\", "); wrapValue(buffer, instname, wrapper.getGenType()); buffer.append(");" + Config.nl); } } } String scopeSave = buffer.toString(); File shellFile = new File(pkgPath, shellName + ".java"); BufferedWriter shell = null; try { FileOutputStream fos = new FileOutputStream(shellFile); shell = new BufferedWriter(new OutputStreamWriter(fos, sourceCharset)); shell.write(packageLine); shell.newLine(); if (imports != null) { shell.write(imports); shell.newLine(); } shell.write("public class "); shell.write(shellName); shell.write(" extends bluej.runtime.Shell {"); shell.newLine(); shell.write("public static "); if (isVoid) { shell.write("void"); } else { shell.write("java.lang.Object"); } shell.write(" run() throws Throwable {"); shell.newLine(); shell.write(vardecl); shell.newLine(); shell.write(invocation); shell.write(scopeSave); if (! isVoid) { shell.write("}"); if (constype != null) { shell.write("} };"); } } shell.newLine(); shell.write("}}"); shell.newLine(); shell.close(); } catch (IOException e) { DialogManager.showErrorFX(parent, "could-not-write-shell-file"); if (shell != null) { try { shell.close(); } catch (IOException ioe) { } } shellFile.delete(); return null; } return shellFile; }
| Write out shell code to retrieve the values of variables or bench objects. | | @param scopePx The scope prefix ("lv:" for local variables) |* @param buffer The string buffer to write the code to * @param isStatic True if the variables should be declared static * @param i An iterator through the variables to write | @param nt The name transform to use | private void writeVariables(String scopePx, StringBuffer buffer, boolean isStatic, Iterator<?> i, NameTransform nt) { for (; i.hasNext();) { NamedValue wrapper = (NamedValue) i.next(); if (wrapper.isInitialized()) { String type = wrapper.getGenType().toString(nt); String instname = wrapper.getName(); if (wrapper.isFinal()) { buffer.append("final "); } if (isStatic) { buffer.append("static "); } buffer.append(type); buffer.append(" " + instname + " = "); extractValue(buffer, scopePx, instname, wrapper.getGenType(), type); buffer.append(Config.nl); } } }
| Get the string to declare a variable. | @param scopePx The scope prefix for the value map | @param isStatic True if the variable should be declared static | @param wrapper The NamedValue representing the variable (and its name) | @param nt The name transform to use for class names | | @return the string to declared the variable | private String getVarDeclString(String scopePx, boolean isStatic, NamedValue wrapper, NameTransform nt) { if (wrapper.isInitialized()) { String type = wrapper.getGenType().toString(nt); String instname = wrapper.getName(); StringBuffer buffer = new StringBuffer(); if (wrapper.isFinal()) { buffer.append("final "); } if (isStatic) { buffer.append("static "); } buffer.append(type); buffer.append(" " + instname + " = "); extractValue(buffer, scopePx, instname, wrapper.getGenType(), type); buffer.append(Config.nl); return buffer.toString(); } else { return ""; } }
| Write code to extract a value of a given type. | @param buffer The buffer in which the expression is stored | @param scopePx The scope prefix ("" for object bench, "lv:" for codepad) |* @param instname The name of the value (used as map key) * @param type The type of the value */ private void extractValue(StringBuffer buffer, String scopePx, String instname, JavaType type, String typeStr) | |{ | |if (type.isPrimitive()) { | |// primitive type. Must pull a wrapped object out, and then | |// unwrap it. | |String castType; | |String extractMethod; | |if (type.typeIs(JavaType.JT_BOOLEAN)) { | |castType = "java.lang.Boolean"; extractMethod = "booleanValue"; } else if (type.typeIs(JavaType.JT_CHAR)) { castType = "java.lang.Character"; extractMethod = "charValue"; } else if (type.typeIs(JavaType.JT_BYTE)) { castType = "java.lang.Byte"; extractMethod = "byteValue"; } else if (type.typeIs(JavaType.JT_SHORT)) { castType = "java.lang.Short"; extractMethod = "shortValue"; } else if (type.typeIs(JavaType.JT_INT)) { castType = "java.lang.Integer"; extractMethod = "intValue"; } else if (type.typeIs(JavaType.JT_LONG)) { castType = "java.lang.Long"; extractMethod = "longValue"; } else if (type.typeIs(JavaType.JT_FLOAT)) { castType = "java.lang.Float"; extractMethod = "floatValue"; } else if (type.typeIs(JavaType.JT_DOUBLE)) { castType = "java.lang.Double"; extractMethod = "doubleValue"; } else { throw new UnsupportedOperationException("unhandled primitive type"); } buffer.append("((" + castType + ")__bluej_runtime_scope.get(\""); buffer.append(scopePx + instname + "\"))." + extractMethod + "();"); } else { // reference (object) type. Much easier. buffer.append("(" + typeStr); buffer.append(")__bluej_runtime_scope.get(\""); buffer.append(scopePx + instname + "\");" + Config.nl); } } /** * Wrap a value, if necessary, as an appropriate object type. * @param buffer The resulting expression is written to this buffer * @param name The name of the variable holding the value | @param type The type of the value | private void wrapValue(StringBuffer buffer, String name, JavaType type) { if (type.isPrimitive()) { if (type.typeIs(JavaType.JT_BOOLEAN)) buffer.append("java.lang.Boolean.valueOf(" + name + ")"); else if (type.typeIs(JavaType.JT_BYTE)) buffer.append("new java.lang.Byte(" + name + ")"); else if (type.typeIs(JavaType.JT_CHAR)) buffer.append("new java.lang.Character(" + name + ")"); else if (type.typeIs(JavaType.JT_DOUBLE)) buffer.append("new java.lang.Double(" + name + ")"); else if (type.typeIs(JavaType.JT_FLOAT)) buffer.append("new java.lang.Float(" + name + ")"); else if (type.typeIs(JavaType.JT_LONG)) buffer.append("new java.lang.Long(" + name + ")"); else if (type.typeIs(JavaType.JT_INT)) buffer.append("new java.lang.Integer(" + name + ")"); else if (type.typeIs(JavaType.JT_SHORT)) buffer.append("new java.lang.Short(" + name + ")"); else { throw new UnsupportedOperationException("unhandled primitive type."); } } else { buffer.append(name); } }
| Start the compilation of a shell fine and register us as a watcher. After | this, we just wait for the callback from the compiler. | private void compileInvocationFile(File shellFile) { File[] files = {shellFile }; compiler.compile(files, new EventqueueCompileObserverAdapter(this)); } @Override public void startCompile(CompileInputFile[] sources, CompileReason reason, CompileType type, int compilationSequence) { }
| | @see bluej.compiler.CompileObserver#compilerMessage(bluej.compiler.Diagnostic) | @Override public boolean compilerMessage(Diagnostic diagnostic, CompileType type) { if (diagnostic.getType() == Diagnostic.ERROR) { if (! gotError) { gotError = true; errorMessage(diagnostic.getFileName(), diagnostic.getStartLine(), diagnostic.getMessage()); return true; } } return false; }
| An error was detected during compilation of the shell class. | private void errorMessage(String filename, long lineNo, String message) { DataCollector.invokeCompileError(pkg, commandString, message); if (dialog != null) { dialog.setErrorMessage("Error: " + message); } watcher.putError(message, ir); }
| The compilation of the shell class has ended. If all went well, execute | now. Then clean up. | @Override @OnThread(Tag.FXPlatform) public synchronized void endCompile(CompileInputFile[] sources, boolean successful, CompileType type, int compilationSequence) { if (dialog != null) { dialog.setWaitCursor(false); if (successful) { closeCallDialog(); } } if (successful) { watcher.beginExecution(ir); startClass(); } else { finishCall(false); } }
| Clean up after an invocation or attempted invocation. | @param successful Whether the invocation compilation was successful | private void finishCall(boolean successful) { deleteShellFiles(); if (!successful && dialog != null) { dialog.setOKEnabled(true); } } @OnThread(Tag.FXPlatform) private void closeCallDialog() { if (dialog != null) { dialog.setWaitCursor(false); dialog.close(); dialog.saveCallHistory(); dialog = null; } if (pkg != null) { pkg.addListener(this); } }
| Remove the shell files that we created for this invocation. | private void deleteShellFiles() { File srcFile = new File(pkgPath, shellName + ".java"); srcFile.delete(); File classFile = new File(pkgPath, shellName + ".class"); classFile.delete(); String [] innerClassFiles = pkgPath.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return (name.startsWith(shellName + "$")); } }); for (String innerClassFile : innerClassFiles) { new File(pkgPath, innerClassFile).delete(); } } | Execute an interactive method call. At this point, the shell class has | been compiled and we are ready to go. | private void startClass() { final String shellClassName = JavaNames.combineNames(pkgName, shellName); new Thread() { public void run() { try { DebuggerResult result = debugger.runClassMain(shellClassName); Platform.runLater(new Runnable() { public void run() { handleResult(result, constructing); finishCall(true); } }); } catch (Throwable e) { e.printStackTrace(System.err); } } }.start(); }
| After an execution has finished, check whether there is a result (such as | a freshly created object, a function result or an exception) and make | sure that it gets processed appropriately. | | <p>"exitStatus" and "result" fields should be set with appropriate values before | * calling this. * * <p>This method is called on the Swing event thread. */ @OnThread(Tag.FXPlatform) public void handleResult(DebuggerResult result, boolean unwrap) | |
{ | |
try { | | // first, check whether we had an unexpected exit | | int status = result.getExitStatus(); | | switch(status) { | | case Debugger.NORMAL_EXIT : | | // resultObj will be the null object representation (isNullObject() == true) for a void call | | DebuggerObject resultObj = result.getResultObject(); | | if (unwrap) { | | // For constructor calls, the result is expected to be the created object. | | resultObj = resultObj.getInstanceField(0).getValueObject(null); | | } | | if (!codepad) | | { | | String resultType; | | //Only record this if it wasn't on behalf of the codepad (codepad records separately): | | if (resultObj.getClassName().startsWith(Shell.class.getCanonicalName())) | | { | | //Wrapped by Shell class, grab from first field. | | if (resultObj.getInstanceField(0).getType().isPrimitive()) | | { | | // If it's a field with primitive type, use type of field: | | resultType = resultObj.getInstanceField(0).getType().toString(); | | } | | else | | { | | // Take type from resulting object: | | resultType = resultObj.getInstanceField(0).getValueObject(null).getClassName(); | | } | | } | | else | | { | | resultType = resultObj.getClassName(); | | if (resultType.equals("")) { resultType = "void"; } } PkgMgrFrame pmf = PkgMgrFrame.findFrame(pkg); DataCollector.invokeMethodSuccess(pkg, commandString, benchName, resultType, pmf == null ? -1 : pmf.getTestIdentifier(), ir.getUniqueIdentifier()); | | } | | ir.setResultObject(resultObj); | | watcher.putResult(resultObj, objName, ir); | | break; | | case Debugger.EXCEPTION : | | ExceptionDescription exc = result.getException(); | | if (!codepad) | | { | | //Only record this if it wasn't on behalf of the codepad (codepad records separately): | | DataCollector.invokeMethodException(pkg, commandString, exc); | | } | | watcher.putException(exc, ir); | | break; | | case Debugger.TERMINATED : // terminated by user | | if (!codepad) | | { | | //Only record this if it wasn't on behalf of the codepad (codepad records separately): | | DataCollector.invokeMethodTerminated(pkg, commandString); | | } | | watcher.putVMTerminated(ir); | | break; | | } // switch | | } | | catch (Throwable e) { | | e.printStackTrace(System.err); | | } | | } | | @Override | | public void graphClosed() | | { | | closeCallDialog(); | | } | | @Override | | public void graphChanged() | | { | | // Nothing needs doing. | | } | | static class CleverQualifyTypeNameTransform | | implements NameTransform | | { | | Package mypackage; | | public CleverQualifyTypeNameTransform(Package p) | | { | | mypackage = p; | | } | | @OnThread(Tag.Any) | | public String transform(String n) | | { | | return cleverQualifyTypeName(mypackage, n); | | } | | } | | @OnThread(Tag.Any) | | static private String cleverQualifyTypeName(Package p, String typeName) | | { | | // if we happen to have a class in this package with the | | // same name as the start of the package, then fully qualifying names | | // does not work ie if the package is Test.Sub and we have | | // a class called Test, then all fully qualified names | | // fail ie new Test.Sub.Test() and new Test.Sub.Foo() | | // in the package where the class Test exists. | | // so in this case we need to use the unqualified name | | // ie new Test() and new Foo() | | // note we need to retain the fully qualified case for when | | // we add library classes etc which may involve constructing | | // objects of types which are not in the current package | | if (!p.isUnnamedPackage()) { | | String pName = p.getQualifiedName(); | | int firstDot = pName.indexOf("."); if (firstDot >= 0) pName = pName.substring(0, firstDot); // if the first part of the package name exists as a target // lets unqualify the typeName if (p.getTarget(pName) != null) | | typeName = JavaNames.getBase(typeName); | | } | | // now we need to deal with nested types | | // these types will have a $ in them but depending on whether | | // they are anonymous classes or member classes will change how | | // we want to refer to them | | // an anonymous class we can refer to as MyClass$1 and the | | // compiler is ok with that | | // a member class, despite being given a type name of | | // MyClass$MemberClass, must be referred to as MyClass.MemberClass | | // in the source code. Here we make this change to the typeName | | // based entirely on whether the character following the $ is | | // a numeral or not (which just happens to be the way it is at | | // the moment but I don't think its written down in the JLS or | | // anything so this may break) | | int firstDollar = typeName.indexOf('$'); | | if (firstDollar != -1) { | | StringBuffer sb = new StringBuffer(typeName); | | // go to length - 1 only so we always have an i+1 character | | // to check. What this means is that if the last character | | // in the typeName is a $, it won't be converted but I don't | | // think a type name with a $ as the last character is valid | | // anyway | | for (int i = firstDollar; i < sb.length() - 1; i++) { | | if (sb.charAt(i) == '$' && !Character.isDigit(sb.charAt(i + 1))) | | sb.setCharAt(i, '.'); | | } | | typeName = sb.toString(); | | } | | return JavaNames.typeName(typeName); | | } | | }

.   Invoker
.   if
.   transform
.   compile
.   Invoker
.   Invoker
.   if
.   Invoker
.   Invoker
.   compile
.   setImports
.   invokeInteractive
.   callDialogOK
.   invokeDirect
.   doInvocation
.   doInvocation
.   run
.   buildArgStrings
.   doFreeFormInvocation
.   writeInvocationFile
.   writeVariables
.   getVarDeclString
.   wrapValue
.   compileInvocationFile
.   startCompile
.   compilerMessage
.   errorMessage
.   endCompile
.   finishCall
.   closeCallDialog
.   deleteShellFiles
.   accept
.   startClass
.   run
.   run




1166 neLoCode + 275 LoComm