Mercurial > hg > truffle
diff graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java @ 21890:894f82515e38
Truffle/APIs and Debugging: Evolutionary steps to integrating debugging and tool support with TruffleVM APIs
- Add a version string to language registration: Language.getShortName() produces a string with both language and version
- Rename SLMain --> SLLanguage (little change current machinery)
- Remove DebugEngine dependence on ExecutionContext: Visualizer access migrated to TruffleLanguage
- ExecutionContext now has only one method left: getCompilerOptions()
- Rename SourceExecutionProvider to DebugSupportProvider, now supplied by implementing abstract TruffleLanguage.getDebugSupport()
- Revise DebugEngine and its helper classes to work with the new APIs
author | Michael Van De Vanter <michael.van.de.vanter@oracle.com> |
---|---|
date | Tue, 09 Jun 2015 15:20:30 -0700 |
parents | graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java@45083be8a812 |
children | ccaf9eb1f5eb |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Tue Jun 09 15:20:30 2015 -0700 @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.sl; + +import java.io.*; +import java.math.*; +import java.net.*; +import java.util.*; +import java.util.Scanner; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.debug.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.vm.*; +import com.oracle.truffle.api.vm.TruffleVM.Symbol; +import com.oracle.truffle.sl.builtins.*; +import com.oracle.truffle.sl.factory.*; +import com.oracle.truffle.sl.nodes.*; +import com.oracle.truffle.sl.nodes.call.*; +import com.oracle.truffle.sl.nodes.controlflow.*; +import com.oracle.truffle.sl.nodes.expression.*; +import com.oracle.truffle.sl.nodes.instrument.*; +import com.oracle.truffle.sl.nodes.local.*; +import com.oracle.truffle.sl.parser.*; +import com.oracle.truffle.sl.runtime.*; +import com.oracle.truffle.tools.*; + +/** + * SL is a simple language to demonstrate and showcase features of Truffle. The implementation is as + * simple and clean as possible in order to help understanding the ideas and concepts of Truffle. + * The language has first class functions, but no object model. + * <p> + * SL is dynamically typed, i.e., there are no type names specified by the programmer. SL is + * strongly typed, i.e., there is no automatic conversion between types. If an operation is not + * available for the types encountered at run time, a type error is reported and execution is + * stopped. For example, {@code 4 - "2"} results in a type error because subtraction is only defined + * for numbers. + * + * <p> + * <b>Types:</b> + * <ul> + * <li>Number: arbitrary precision integer numbers. The implementation uses the Java primitive type + * {@code long} to represent numbers that fit into the 64 bit range, and {@link BigInteger} for + * numbers that exceed the range. Using a primitive type such as {@code long} is crucial for + * performance. + * <li>Boolean: implemented as the Java primitive type {@code boolean}. + * <li>String: implemented as the Java standard type {@link String}. + * <li>Function: implementation type {@link SLFunction}. + * <li>Null (with only one value {@code null}): implemented as the singleton + * {@link SLNull#SINGLETON}. + * </ul> + * The class {@link SLTypes} lists these types for the Truffle DSL, i.e., for type-specialized + * operations that are specified using Truffle DSL annotations. + * + * <p> + * <b>Language concepts:</b> + * <ul> + * <li>Literals for {@link SLBigIntegerLiteralNode numbers} , {@link SLStringLiteralNode strings}, + * and {@link SLFunctionLiteralNode functions}. + * <li>Basic arithmetic, logical, and comparison operations: {@link SLAddNode +}, {@link SLSubNode + * -}, {@link SLMulNode *}, {@link SLDivNode /}, {@link SLLogicalAndNode logical and}, + * {@link SLLogicalOrNode logical or}, {@link SLEqualNode ==}, !=, {@link SLLessThanNode <}, + * {@link SLLessOrEqualNode ≤}, >, ≥. + * <li>Local variables: local variables must be defined (via a {@link SLWriteLocalVariableNode + * write}) before they can be used (by a {@link SLReadLocalVariableNode read}). Local variables are + * not visible outside of the block where they were first defined. + * <li>Basic control flow statements: {@link SLBlockNode blocks}, {@link SLIfNode if}, + * {@link SLWhileNode while} with {@link SLBreakNode break} and {@link SLContinueNode continue}, + * {@link SLReturnNode return}. + * <li>Function calls: {@link SLInvokeNode invocations} are efficiently implemented with + * {@link SLDispatchNode polymorphic inline caches}. + * </ul> + * + * <p> + * <b>Syntax and parsing:</b><br> + * The syntax is described as an attributed grammar. The {@link Parser} and {@link Scanner} are + * automatically generated by the parser generator Coco/R (available from <a + * href="http://ssw.jku.at/coco/">http://ssw.jku.at/coco/</a>). The grammar contains semantic + * actions that build the AST for a method. To keep these semantic actions short, they are mostly + * calls to the {@link SLNodeFactory} that performs the actual node creation. All functions found in + * the SL source are added to the {@link SLFunctionRegistry}, which is accessible from the + * {@link SLContext}. + * + * <p> + * <b>Builtin functions:</b><br> + * Library functions that are available to every SL source without prior definition are called + * builtin functions. They are added to the {@link SLFunctionRegistry} when the {@link SLContext} is + * created. There current builtin functions are + * <ul> + * <li>{@link SLReadlnBuiltin readln}: Read a String from the {@link SLContext#getInput() standard + * input}. + * <li>{@link SLPrintlnBuiltin println}: Write a value to the {@link SLContext#getOutput() standard + * output}. + * <li>{@link SLNanoTimeBuiltin nanoTime}: Returns the value of a high-resolution time, in + * nanoseconds. + * <li>{@link SLDefineFunctionBuiltin defineFunction}: Parses the functions provided as a String + * argument and adds them to the function registry. Functions that are already defined are replaced + * with the new version. + * </ul> + * + * <p> + * <b>Tools:</b><br> + * The use of some of Truffle's support for developer tools (based on the Truffle Instrumentation + * Framework) are demonstrated in this file, for example: + * <ul> + * <li>a {@linkplain NodeExecCounter counter for node executions}, tabulated by node type; and</li> + * <li>a simple {@linkplain CoverageTracker code coverage engine}.</li> + * </ul> + * In each case, the tool is enabled if a corresponding local boolean variable in this file is set + * to {@code true}. Results are printed at the end of the execution using each tool's + * <em>default printer</em>. + * + */ +@TruffleLanguage.Registration(name = "SL", version = "0.5", mimeType = "application/x-sl") +public class SLLanguage extends TruffleLanguage { + private static SLLanguage LAST; + private static List<NodeFactory<? extends SLBuiltinNode>> builtins = Collections.emptyList(); + private static Visualizer visualizer = new SLDefaultVisualizer(); + private static ASTProber registeredASTProber; // non-null if prober already registered + private final SLContext context; + private DebugSupportProvider debugSupport; + + public SLLanguage(Env env) { + super(env); + context = SLContextFactory.create(new BufferedReader(env().stdIn()), new PrintWriter(env().stdOut(), true)); + LAST = this; + for (NodeFactory<? extends SLBuiltinNode> builtin : builtins) { + context.installBuiltin(builtin); + } + } + + // TODO (mlvdv) command line options + /* Enables demonstration of per-type tabulation of node execution counts */ + private static boolean nodeExecCounts = false; + /* Enables demonstration of per-line tabulation of STATEMENT node execution counts */ + private static boolean statementCounts = false; + /* Enables demonstration of per-line tabulation of STATEMENT coverage */ + private static boolean coverage = false; + + /* Small tools that can be installed for demonstration */ + private static NodeExecCounter nodeExecCounter = null; + private static NodeExecCounter statementExecCounter = null; + private static CoverageTracker coverageTracker = null; + + /** + * The main entry point. Use the mx command "mx sl" to run it with the correct class path setup. + */ + public static void main(String[] args) throws IOException { + TruffleVM vm = TruffleVM.newVM().build(); + assert vm.getLanguages().containsKey("application/x-sl"); + + setupToolDemos(); + + int repeats = 1; + if (args.length >= 2) { + repeats = Integer.parseInt(args[1]); + } + + if (args.length == 0) { + vm.eval("application/x-sl", new InputStreamReader(System.in)); + } else { + vm.eval(new File(args[0]).toURI()); + } + Symbol main = vm.findGlobalSymbol("main"); + if (main == null) { + throw new SLException("No function main() defined in SL source file."); + } + while (repeats-- > 0) { + main.invoke(null); + } + reportToolDemos(); + } + + /** + * Temporary method during API evolution, supports debugger integration. + */ + public static void run(Source source) throws IOException { + TruffleVM vm = TruffleVM.newVM().build(); + assert vm.getLanguages().containsKey("application/x-sl"); + vm.eval(new File(source.getPath()).toURI()); + Symbol main = vm.findGlobalSymbol("main"); + if (main == null) { + throw new SLException("No function main() defined in SL source file."); + } + main.invoke(null); + } + + /** + * Parse and run the specified SL source. Factored out in a separate method so that it can also + * be used by the unit test harness. + */ + public static long run(TruffleVM context, URI source, PrintWriter logOutput, PrintWriter out, int repeats, List<NodeFactory<? extends SLBuiltinNode>> currentBuiltins) throws IOException { + builtins = currentBuiltins; + + if (logOutput != null) { + logOutput.println("== running on " + Truffle.getRuntime().getName()); + // logOutput.println("Source = " + source.getCode()); + } + + /* Parse the SL source file. */ + Object result = context.eval(source); + if (result != null) { + out.println(result); + } + + /* Lookup our main entry point, which is per definition always named "main". */ + Symbol main = context.findGlobalSymbol("main"); + if (main == null) { + throw new SLException("No function main() defined in SL source file."); + } + + /* Change to true if you want to see the AST on the console. */ + boolean printASTToLog = false; + /* Change to true if you want to see source attribution for the AST to the console */ + boolean printSourceAttributionToLog = false; + /* Change to dump the AST to IGV over the network. */ + boolean dumpASTToIGV = false; + + printScript("before execution", LAST.context, logOutput, printASTToLog, printSourceAttributionToLog, dumpASTToIGV); + long totalRuntime = 0; + try { + for (int i = 0; i < repeats; i++) { + long start = System.nanoTime(); + /* Call the main entry point, without any arguments. */ + try { + result = main.invoke(null); + if (result != null) { + out.println(result); + } + } catch (UnsupportedSpecializationException ex) { + out.println(formatTypeError(ex)); + } catch (SLUndefinedFunctionException ex) { + out.println(String.format("Undefined function: %s", ex.getFunctionName())); + } + long end = System.nanoTime(); + totalRuntime += end - start; + + if (logOutput != null && repeats > 1) { + logOutput.println("== iteration " + (i + 1) + ": " + ((end - start) / 1000000) + " ms"); + } + } + + } finally { + printScript("after execution", LAST.context, logOutput, printASTToLog, printSourceAttributionToLog, dumpASTToIGV); + } + return totalRuntime; + } + + /** + * When dumpASTToIGV is true: dumps the AST of all functions to the IGV visualizer, via a socket + * connection. IGV can be started with the mx command "mx igv". + * <p> + * When printASTToLog is true: prints the ASTs to the console. + */ + private static void printScript(String groupName, SLContext context, PrintWriter logOutput, boolean printASTToLog, boolean printSourceAttributionToLog, boolean dumpASTToIGV) { + if (dumpASTToIGV) { + GraphPrintVisitor graphPrinter = new GraphPrintVisitor(); + graphPrinter.beginGroup(groupName); + for (SLFunction function : context.getFunctionRegistry().getFunctions()) { + RootCallTarget callTarget = function.getCallTarget(); + if (callTarget != null) { + graphPrinter.beginGraph(function.toString()).visit(callTarget.getRootNode()); + } + } + graphPrinter.printToNetwork(true); + } + if (printASTToLog && logOutput != null) { + for (SLFunction function : context.getFunctionRegistry().getFunctions()) { + RootCallTarget callTarget = function.getCallTarget(); + if (callTarget != null) { + logOutput.println("=== " + function); + NodeUtil.printTree(logOutput, callTarget.getRootNode()); + } + } + } + if (printSourceAttributionToLog && logOutput != null) { + for (SLFunction function : context.getFunctionRegistry().getFunctions()) { + RootCallTarget callTarget = function.getCallTarget(); + if (callTarget != null) { + logOutput.println("=== " + function); + NodeUtil.printSourceAttributionTree(logOutput, callTarget.getRootNode()); + } + } + } + } + + /** + * Provides a user-readable message for run-time type errors. SL is strongly typed, i.e., there + * are no automatic type conversions of values. Therefore, Truffle does the type checking for + * us: if no matching node specialization for the actual values is found, then we have a type + * error. Specialized nodes use the {@link UnsupportedSpecializationException} to report that no + * specialization was found. We therefore just have to convert the information encapsulated in + * this exception in a user-readable form. + */ + private static String formatTypeError(UnsupportedSpecializationException ex) { + StringBuilder result = new StringBuilder(); + result.append("Type error"); + if (ex.getNode() != null && ex.getNode().getSourceSection() != null) { + SourceSection ss = ex.getNode().getSourceSection(); + if (ss != null && !(ss instanceof NullSourceSection)) { + result.append(" at ").append(ss.getSource().getShortName()).append(" line ").append(ss.getStartLine()).append(" col ").append(ss.getStartColumn()); + } + } + result.append(": operation"); + if (ex.getNode() != null) { + NodeInfo nodeInfo = SLContext.lookupNodeInfo(ex.getNode().getClass()); + if (nodeInfo != null) { + result.append(" \"").append(nodeInfo.shortName()).append("\""); + } + } + result.append(" not defined for"); + + String sep = " "; + for (int i = 0; i < ex.getSuppliedValues().length; i++) { + Object value = ex.getSuppliedValues()[i]; + Node node = ex.getSuppliedNodes()[i]; + if (node != null) { + result.append(sep); + sep = ", "; + + if (value instanceof Long || value instanceof BigInteger) { + result.append("Number ").append(value); + } else if (value instanceof Boolean) { + result.append("Boolean ").append(value); + } else if (value instanceof String) { + result.append("String \"").append(value).append("\""); + } else if (value instanceof SLFunction) { + result.append("Function ").append(value); + } else if (value == SLNull.SINGLETON) { + result.append("NULL"); + } else if (value == null) { + // value is not evaluated because of short circuit evaluation + result.append("ANY"); + } else { + result.append(value); + } + } + } + return result.toString(); + } + + @Override + protected Object eval(Source code) throws IOException { + try { + context.evalSource(code); + } catch (Exception e) { + throw new IOException(e); + } + return null; + } + + @Override + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { + for (SLFunction f : context.getFunctionRegistry().getFunctions()) { + if (globalName.equals(f.getName())) { + return f; + } + } + return null; + } + + @Override + protected Object getLanguageGlobal() { + return context; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return object instanceof SLFunction; + } + + @Override + protected ToolSupportProvider getToolSupport() { + return getDebugSupport(); + } + + @Override + protected DebugSupportProvider getDebugSupport() { + if (debugSupport == null) { + debugSupport = new SLDebugProvider(); + } + return debugSupport; + } + + // TODO (mlvdv) remove the static hack when we no longer have the static demo variables + private static void setupToolDemos() { + if (statementCounts || coverage) { + if (registeredASTProber == null) { + registeredASTProber = new SLStandardASTProber(); + // This should be registered on the TruffleVM + Probe.registerASTProber(registeredASTProber); + } + } + if (nodeExecCounts) { + nodeExecCounter = new NodeExecCounter(); + nodeExecCounter.install(); + } + + if (statementCounts) { + statementExecCounter = new NodeExecCounter(StandardSyntaxTag.STATEMENT); + statementExecCounter.install(); + } + + if (coverage) { + coverageTracker = new CoverageTracker(); + coverageTracker.install(); + } + } + + private static void reportToolDemos() { + if (nodeExecCounter != null) { + nodeExecCounter.print(System.out); + nodeExecCounter.dispose(); + } + if (statementExecCounter != null) { + statementExecCounter.print(System.out); + statementExecCounter.dispose(); + } + if (coverageTracker != null) { + coverageTracker.print(System.out); + coverageTracker.dispose(); + } + } + + private final class SLDebugProvider implements DebugSupportProvider { + + public SLDebugProvider() { + if (registeredASTProber == null) { + registeredASTProber = new SLStandardASTProber(); + // This should be registered on the TruffleVM + Probe.registerASTProber(registeredASTProber); + } + } + + public Visualizer getVisualizer() { + if (visualizer == null) { + visualizer = new SLDefaultVisualizer(); + } + return visualizer; + } + + public void enableASTProbing(ASTProber prober) { + if (prober != null) { + // This should be registered on the TruffleVM + Probe.registerASTProber(prober); + } + } + + public void run(Source source) throws DebugSupportException { + // TODO (mlvdv) fix to run properly in the current VM + try { + SLLanguage.run(source); + } catch (Exception e) { + throw new DebugSupportException(e); + } + } + + public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException { + throw new DebugSupportException("evalInContext not supported in this language"); + } + + public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException { + throw new DebugSupportException("createAdvancedInstrumentRootFactory not supported in this language"); + } + + } + +}