/*
* This file is part of the OpenJML project.
* Author: David R. Cok
*/
package org.jmlspecs.openjml;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import org.jmlspecs.openjml.JmlTree.JmlCompilationUnit;
import org.jmlspecs.openjml.JmlTree.JmlTypeClause;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.comp.JmlAttr;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.jvm.ClassReader;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.parser.JmlDebugTreePrinter;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;
/**
* This class executes the OpenJML tool interactively; call the main routine to
* begin the interactive command loop. See the list of implemented commands in
* the 'command' method.
*
* @author David Cok
*/
// FIXME - review whether to support this; review this class
public class Interactive extends Main {
/** This is the main entry point for the application
* @param args the non-null array of command line argument
*/
//@ requires args != null && \nonnullelements(args);
//@ diverges true;
public static void main(String[] args) {
try {
System.exit(new Interactive().run(args));
} catch (Exception e) {
System.err.println("Failed with exception " + e);
}
}
/** The constructor for an object of this class, initiating the base class
* with System.out as the output location. */
public Interactive() throws Exception {
super("jml-interactive", new PrintWriter(System.out, true), null, null);
}
/** The top-level routine that implements the interactive command processing.
*
* @param args the arguments as supplied on the command line
* @return 0
*/
//@ requires args != null && \nonnullelements(args);
public int run(String[] args) {
setup(args);
commandLoop();
System.out.println("... exiting");
return 0;
}
// FIXME - finish documentation
public JavacFileManager fileManager;
public void setup(String[] args) {
context = new Context();
JavacFileManager.preRegister(context); // can't create it until Log has been set up
compile(args, context);
fileManager = (JavacFileManager)context.get(JavaFileManager.class);
//filemanager = (JavacFileManager)
// if (fileManager instanceof JavacFileManager) {
// // A fresh context was created above, so jfm must be a JavacFileManager
// ((JavacFileManager)fileManager).close();
// }
}
/** This method executes a command loop; each iteration does the following:
* <UL>
* <LI> prints a prompt to System.out
* <LI> reads a string from System.in (terminated by a newline)
* <LI> executes that string as a command (calling method command)
* </UL>
* The loop terminates when (a) the command method returns -1, (b) an
* exception occurs when reading input or executing the command.
*/
public void commandLoop() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = "";
while (str != null) {
System.out.print("> ");
str = in.readLine();
try {
int c = command(str);
if (c == -1) break;
} catch (Exception e) {
System.out.println("... Exception during command: " + e);
e.printStackTrace(System.out);
}
}
} catch (IOException e) {
System.out.println(" .... failed to perform IO: " + e);
}
}
/** This executes an interactive command; it returns -1 if the command loop
* should exit. If the input command is null, -1 is returned.
* @param command the possibly null input command
* @return -1 if the command loop is to be exited, non-negative otherwise
* @throws Exception
*/
//@ ensures command == null ==> \result == -1;
public int command(String command) throws Exception {
int e = 0;
if (command == null) return -1;
if (command.length() == 0) return 0;
if (command.startsWith("quit")) return -1;
if (command.startsWith("help")) return help(command);
if (command.startsWith("parse")) return parse(command);
if (command.startsWith("check")) return check(command);
if (command.startsWith("reset")) return reset(command);
if (command.startsWith("cclass")) return cclass(command);
if (command.startsWith("specs")) return specs(command);
System.out.println("UNKNOWN COMMAND: " + command);
return e;
}
/** Executes the help command
*
* @param command - ignored, but should be "help"
* @return 0
*/
public int help(String command) {
System.out.println(" Implemented commands:");
System.out.println(" help - lists the commands");
System.out.println(" quit - terminates the program");
System.out.println(" CTRL-Z - terminates the program");
System.out.println(" parse <file> - parses the given file");
System.out.println(" check <file> - parses and typechecks the given file");
System.out.println(" reset - restarts with a new compilation context");
System.out.println(" cclass - parses and typechecks a class by fully-qualified name");
System.out.println(" specs - shows specs for a class by fully-qualified name");
return 0;
}
public int parse(String command) {
int n = command.indexOf(' ');
String[] files = command.substring(n+1).split(" ");
for (String f: files) {
JavaFileObject j = fileManager.getRegularFile(new File(f));
JmlCompilationUnit cu = (JmlCompilationUnit)JmlCompiler.instance(context).parse(j);
cu.accept(new JmlDebugTreePrinter(System.out,null)); // FIXME - this prints debug information - is that what we want?
}
return 0;
}
public int check(String command) throws Exception {
int n = command.indexOf(' ');
String[] files = command.substring(n+1).split(" ");
for (String f: files) { // FIXME - do in one swoop
JmlCompiler jmlCompiler = (JmlCompiler)JmlCompiler.instance(context);
JavaFileObject j = fileManager.getRegularFile(new File(f));
jmlCompiler.compile(List.of(j),List.<String>nil(),null);
// FIXME - do we really need to expose compilerKey? if not, put it back to protected
context.put(JavaCompiler.compilerKey,(JavaCompiler)null);
JmlCompiler.preRegister(context);
Log.instance(context).nerrors = 0;
Log.instance(context).nwarnings = 0;
}
return 0;
}
public int reset(String command) throws Exception {
String[] args = command.substring(command.indexOf(" ")).split(" ");
setup(args);
return 0;
}
public int cclass(String command) throws Exception {
String[] args = command.substring(command.indexOf(" ")).split(" ");
JmlSpecs.instance(context).initializeSpecsPath(); // FIXME - should not this happen in setup
for (String s: args) {
if (s.length() == 0) continue;
Name name = Names.instance(context).fromString(s);
ClassSymbol csym = ClassReader.instance(context).enterClass(name);
csym.complete();
JmlSpecs.TypeSpecs tsp = JmlSpecs.instance(context).get(csym);
if (tsp == null) {
((JmlCompiler)JmlCompiler.instance(context)).loadSpecsForBinary(null,csym);
}
JmlAttr.instance(context).attribClass(csym);
}
return 0;
}
public int specs(String command) throws Exception {
String[] args = command.substring(command.indexOf(" ")).split(" ");
JmlSpecs.instance(context).initializeSpecsPath(); // FIXME - should not this happen in setup
for (String s: args) {
if (s.length() == 0) continue;
Name name = Names.instance(context).fromString(s);
ClassSymbol csym = ClassReader.instance(context).enterClass(name);
csym.complete();
JmlSpecs.TypeSpecs tsp = JmlSpecs.instance(context).get(csym);
if (tsp == null) {
((JmlCompiler)JmlCompiler.instance(context)).loadSpecsForBinary(null,csym);
}
JmlAttr.instance(context).attribClass(csym);
tsp = JmlSpecs.instance(context).get(csym);
if (tsp == null) {
System.out.println("... Could not find specs for " + name);
} else {
System.out.println(" Specifications for " + name);
if (tsp.file == null) System.out.println(" No source file given");
if (tsp.file != null) System.out.println(" Source: " + tsp.file);
if (tsp.modifiers == null) System.out.println(" No modifiers object present");
else {
System.out.println(" Java modifiers: " + Flags.toString(tsp.modifiers.flags));
System.out.print(" Annotations:");
for (JCAnnotation a: tsp.modifiers.annotations) {
System.out.print(" " + a);
}
System.out.println();
}
if (tsp.clauses == null) System.out.println(" No clause list");
else {
System.out.println(" " + tsp.clauses.size() + " clauses");
for (JmlTypeClause t : tsp.clauses) {
System.out.println(" " + t);
}
}
if (tsp.methods == null) System.out.println(" No method list");
else {
System.out.println(" " + tsp.methods.size() + " methods");
for (MethodSymbol m : tsp.methods.keySet()) {
JmlSpecs.MethodSpecs sp = tsp.methods.get(m);
System.out.println(" " + m);
System.out.println(sp.mods);
System.out.println(sp.cases);
}
}
if (tsp.fields == null) System.out.println(" No fields list");
else {
System.out.println(" " + tsp.fields.size() + " fields");
for (VarSymbol f : tsp.fields.keySet()) {
JmlSpecs.FieldSpecs sp = tsp.fields.get(f);
System.out.println(" " + f);
}
}
}
}
return 0;
}
}