/******************************************************************************* * Copyright (c) 2000, 2010 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.core.tests.eval; import java.io.File; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.tests.runtime.LocalVMLauncher; import org.eclipse.jdt.core.tests.runtime.LocalVirtualMachine; import org.eclipse.jdt.core.tests.runtime.TargetException; import org.eclipse.jdt.core.tests.runtime.TargetInterface; import org.eclipse.jdt.core.tests.util.Util; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.IProblemFactory; import org.eclipse.jdt.internal.compiler.batch.FileSystem; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.env.IBinaryField; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.eval.EvaluationContext; import org.eclipse.jdt.internal.eval.EvaluationResult; import org.eclipse.jdt.internal.eval.GlobalVariable; import org.eclipse.jdt.internal.eval.IRequestor; public class SimpleTest { static final String JRE_PATH = Util.getJREDirectory(); static final String[] COMPILATION_CLASSPATH = Util.concatWithClassLibs(Util.getOutputDirectory(), false); static final String[] RUNTIME_CLASSPATH = new String[] {Util.getOutputDirectory()}; static final String TARGET_PATH = Util.getOutputDirectory() + File.separator + "evaluation"; protected EvaluationContext context; protected LocalVirtualMachine launchedVM; protected TargetInterface target; protected Requestor requestor; class Requestor implements IRequestor { int globalProblemCount = 0; public boolean acceptClassFiles(ClassFile[] classFiles, char[] codeSnippetClassName) { try { SimpleTest.this.target.sendClasses(codeSnippetClassName != null, classFiles); } catch (TargetException e) { return false; } if (codeSnippetClassName != null) { TargetInterface.Result result = SimpleTest.this.target.getResult(); if (result.displayString == null) { System.out.println("(No explicit return value)"); } else { System.out.print("("); System.out.print(result.typeName); System.out.print(") "); System.out.println(result.displayString); } } else { for (int i = 0, length = classFiles.length; i < length; i++) { char[][] compoundName = classFiles[i].getCompoundName(); if (new String(compoundName[compoundName.length-1]).startsWith("GlobalVariable")) { try { IBinaryField[] fields = new ClassFileReader(classFiles[i].getBytes(), null).getFields(); if (fields != null) { for (int j = 0; j < fields.length; j++) { TargetInterface.Result result = SimpleTest.this.target.getResult(); if (result.displayString == null) { System.out.println("(No explicit return value)"); } else { System.out.print("("); System.out.print(result.typeName); System.out.print(") "); System.out.println(result.displayString); } } } } catch (ClassFormatException e) { e.printStackTrace(); } } } } return true; } public void acceptProblem(CategorizedProblem problem, char[] fragmentSource, int fragmentKind) { int localErrorCount = 0; this.globalProblemCount++; char[] source = fragmentSource; if (localErrorCount == 0) System.out.println("----------"); if (fragmentKind == EvaluationResult.T_INTERNAL) { System.out.print(this.globalProblemCount + (problem.isError() ? ". INTERNAL ERROR" : ". INTERNAL WARNING")); System.out.print(" in generated compilation unit"); } else { System.out.print(this.globalProblemCount + (problem.isError() ? ". ERROR" : ". WARNING")); System.out.print(" in "); switch (fragmentKind) { case EvaluationResult.T_PACKAGE: System.out.print("package"); break; case EvaluationResult.T_IMPORT: System.out.print("import"); break; case EvaluationResult.T_CODE_SNIPPET: System.out.print("code snippet"); break; case EvaluationResult.T_VARIABLE: int line = problem.getSourceLineNumber(); if (line == -1) { System.out.print("variable type"); source = findVar(fragmentSource).getTypeName(); } else if (line == 0) { System.out.print("variable name"); source = findVar(fragmentSource).getName(); } else { System.out.print("variable initializer"); source = findVar(fragmentSource).getInitializer(); } break; } } System.out.println(errorReportSource((DefaultProblem)problem, source)); System.out.println(problem.getMessage()); System.out.println("----------"); if (problem.isError()) localErrorCount++; } } /** * Build a char array from the given lines */ protected char[] buildCharArray(String[] lines) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < lines.length; i++) { buffer.append(lines[i]); if (i < lines.length -1) { buffer.append("\n"); } } int length = buffer.length(); char[] result = new char[length]; buffer.getChars(0, length, result, 0); return result; } protected String errorReportSource(DefaultProblem problem, char[] source) { //extra from the source the innacurate token //and "highlight" it using some underneath ^^^^^ //put some context around too. //this code assumes that the font used in the console is fixed size //sanity ..... if ((problem.getSourceStart() > problem.getSourceEnd()) || ((problem.getSourceStart() < 0) && (problem.getSourceEnd() < 0))) return "\n!! no source information available !!"; //regular behavior....(slow code) final char SPACE = '\u0020'; final char MARK = '^'; final char TAB = '\t'; //the next code tries to underline the token..... //it assumes (for a good display) that token source does not //contain any \r \n. This is false on statements ! //(the code still works but the display is not optimal !) //compute the how-much-char we are displaying around the inaccurate token int begin = problem.getSourceStart() >= source.length ? source.length - 1 : problem.getSourceStart(); int relativeStart = 0; int end = problem.getSourceEnd() >= source.length ? source.length - 1 : problem.getSourceEnd(); label : for (relativeStart = 0;; relativeStart++) { if (begin == 0) break label; if ((source[begin - 1] == '\n') || (source[begin - 1] == '\r')) break label; begin--; } label : for (;;) { if ((end + 1) >= source.length) break label; if ((source[end + 1] == '\r') || (source[end + 1] == '\n')) { break label; } end++; } //extract the message form the source char[] extract = new char[end - begin + 1]; System.arraycopy(source, begin, extract, 0, extract.length); char c; //remove all SPACE and TAB that begin the error message... int trimLeftIndex = 0; while (((c = extract[trimLeftIndex++]) == TAB) || (c == SPACE)) { } System.arraycopy(extract, trimLeftIndex - 1, extract = new char[extract.length - trimLeftIndex + 1], 0, extract.length); relativeStart -= trimLeftIndex; //buffer spaces and tabs in order to reach the error position int pos = 0; char[] underneath = new char[extract.length]; // can't be bigger for (int i = 0; i <= relativeStart; i++) { if (extract[i] == TAB) { underneath[pos++] = TAB; } else { underneath[pos++] = SPACE; } } //mark the error position for (int i = problem.getSourceStart(); i <= (problem.getSourceEnd() >= source.length ? source.length - 1 : problem.getSourceEnd()); i++) underneath[pos++] = MARK; //resize underneathto remove 'null' chars System.arraycopy(underneath, 0, underneath = new char[pos], 0, pos); return ((problem.getSourceLineNumber() > 0) ? (" (at line " + String.valueOf(problem.getSourceLineNumber()) + ")") : "" ) + "\n\t" + new String(extract) + "\n\t" + new String(underneath); } protected GlobalVariable findVar(char[] varName) { GlobalVariable[] vars = this.context.allVariables(); for (int i = 0; i < vars.length; i++) { GlobalVariable var = vars[i]; if (CharOperation.equals(var.getName(), varName)) { return var; } } return null; } protected INameEnvironment getEnv() { return new FileSystem(COMPILATION_CLASSPATH, new String[0], null); } protected IProblemFactory getProblemFactory() { return new DefaultProblemFactory(java.util.Locale.getDefault()); } protected void startEvaluationContext() throws TargetException { LocalVMLauncher launcher = LocalVMLauncher.getLauncher(); launcher.setVMPath(JRE_PATH); launcher.setClassPath(RUNTIME_CLASSPATH); int evalPort = Util.getFreePort(); launcher.setEvalPort(evalPort); launcher.setEvalTargetPath(TARGET_PATH); this.launchedVM = launcher.launch(); (new Thread() { public void run() { try { java.io.InputStream in = SimpleTest.this.launchedVM.getInputStream(); int read = 0; while (read != -1) { try { read = in.read(); } catch (java.io.IOException e) { read = -1; } if (read != -1) { System.out.print((char)read); } } } catch (TargetException e) { } } }).start(); (new Thread() { public void run() { try { java.io.InputStream in = SimpleTest.this.launchedVM.getErrorStream(); int read = 0; while (read != -1) { try { read = in.read(); } catch (java.io.IOException e) { read = -1; } if (read != -1) { System.out.print((char)read); } } } catch (TargetException e) { } } }).start(); this.requestor = new Requestor(); this.target = new TargetInterface(); this.target.connect("localhost", evalPort, 30000); // allow 30s max to connect (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=188127) this.context = new EvaluationContext(); } protected void stopEvaluationContext() { try { this.target.disconnect(); // Close the socket first so that the OS resource has a chance to be freed. int retry = 0; while (this.launchedVM.isRunning() && (++retry < 20)) { try { Thread.sleep(retry * 100); } catch (InterruptedException e) { } } if (this.launchedVM.isRunning()) { this.launchedVM.shutDown(); } this.context = null; } catch (TargetException e) { throw new Error(e.getMessage()); } } }