diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java new file mode 100644 index 0000000000000000000000000000000000000000..5420becd7a23b9ca41c63fa8f75858f9aa833a65 --- /dev/null +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/EngineRootNode.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018, 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.r.engine; + +import java.util.List; +import java.util.Set; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage.ContextReference; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrumentation.InstrumentableNode; +import com.oracle.truffle.api.instrumentation.ProbeNode; +import com.oracle.truffle.api.instrumentation.Tag; +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.ExplodeLoop; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.r.runtime.ExitException; +import com.oracle.truffle.r.runtime.JumpToTopLevelException; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RSource; +import com.oracle.truffle.r.runtime.ReturnException; +import com.oracle.truffle.r.runtime.Utils.DebugExitException; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.interop.R2Foreign; +import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen; +import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; + +class EngineRootNode extends RootNode { + private final SourceSection sourceSection; + + private final MaterializedFrame executionFrame; + private final ContextReference<RContext> contextReference; + + @Child private EngineBodyNode bodyNode; + @Child private R2Foreign r2Foreign = R2ForeignNodeGen.create(); + + EngineRootNode(EngineBodyNode bodyNode, RContext context, SourceSection sourceSection, MaterializedFrame executionFrame) { + super(context.getLanguage()); + this.sourceSection = sourceSection; + this.bodyNode = bodyNode; + this.executionFrame = executionFrame; + this.contextReference = context.getLanguage().getContextReference(); + } + + public static EngineRootNode createEngineRoot(REngine engine, RContext context, List<RSyntaxNode> statements, SourceSection sourceSection, MaterializedFrame executionFrame) { + return new EngineRootNode(new EngineBodyNode(engine, statements, getPrintResult(sourceSection)), context, sourceSection, executionFrame); + } + + /** + * The normal {@link REngine#doMakeCallTarget} happens first, then we actually run the call + * using the standard FastR machinery, saving and restoring the {@link RContext}, since we have + * no control over what that might be when the call is initiated. + */ + @Override + public Object execute(VirtualFrame frame) { + Object actualFrame = executionFrame != null ? executionFrame : contextReference.get().stateREnvironment.getGlobalFrame(); + try { + return r2Foreign.execute(this.bodyNode.execute(actualFrame)); + } catch (ReturnException ex) { + return ex.getResult(); + } catch (DebugExitException | JumpToTopLevelException | ExitException | ThreadDeath e) { + CompilerDirectives.transferToInterpreter(); + throw e; + } catch (RError e) { + CompilerDirectives.transferToInterpreter(); + throw e; + } catch (Throwable t) { + CompilerDirectives.transferToInterpreter(); + // other errors didn't produce an output yet + RInternalError.reportError(t); + throw t; + } + } + + @Override + public SourceSection getSourceSection() { + return sourceSection; + } + + public static boolean isEngineBody(Object node) { + return node instanceof EngineBodyNode; + } + + private static boolean getPrintResult(SourceSection sourceSection) { + // can't print if initializing the system in embedded mode (no builtins yet) + return !sourceSection.getSource().getName().equals(RSource.Internal.INIT_EMBEDDED.string) && sourceSection.getSource().isInteractive(); + } + + private static final class EngineBodyNode extends Node implements InstrumentableNode { + + private final REngine engine; + private final List<RSyntaxNode> statements; + @Children protected final DirectCallNode[] calls; + private final boolean printResult; + + EngineBodyNode(REngine engine, List<RSyntaxNode> statements, boolean printResult) { + this.engine = engine; + this.statements = statements; + this.calls = new DirectCallNode[statements.size()]; + this.printResult = printResult; + } + + @ExplodeLoop + Object execute(Object actualFrame) { + Object lastValue = RNull.instance; + int lastStatus = 0; + for (int i = 0; i < calls.length; i++) { + materializeCall(i); + lastValue = calls[i].call(new Object[]{actualFrame}); + } + return lastValue; + } + + @Override + public boolean isInstrumentable() { + return false; + } + + @Override + public WrapperNode createWrapper(ProbeNode probe) { + return null; + } + + @Override + public boolean hasTag(Class<? extends Tag> tag) { + return false; + } + + @Override + public InstrumentableNode materializeInstrumentableNodes(Set<Class<? extends Tag>> materializedTags) { + for (int i = 0; i < calls.length; i++) { + materializeCall(i); + } + return this; + } + + protected void materializeCall(int i) { + if (calls[i] == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + RNode node = statements.get(i).asRNode(); + calls[i] = insert(Truffle.getRuntime().createDirectCallNode(engine.doMakeCallTarget(node, RSource.Internal.REPL_WRAPPER.string, printResult, true))); + } + } + } +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java index 371e16066c0e012dc4f5fc198061ad7b30226b0b..8bfbe34c6cad6cb7fdc336d9096ea8256a675d21 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java @@ -22,6 +22,11 @@ */ package com.oracle.truffle.r.engine; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -33,16 +38,14 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleLanguage.ContextReference; import com.oracle.truffle.api.dsl.UnsupportedSpecializationException; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; -import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.ExecutableNode; -import com.oracle.truffle.api.nodes.ExplodeLoop; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.api.source.Source; @@ -78,6 +81,7 @@ import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RSource; import com.oracle.truffle.r.runtime.ReturnException; import com.oracle.truffle.r.runtime.RootWithBody; +import com.oracle.truffle.r.runtime.RootBodyNode; import com.oracle.truffle.r.runtime.ThreadTimings; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.Utils.DebugExitException; @@ -97,7 +101,6 @@ import com.oracle.truffle.r.runtime.data.RTypedValue; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.interop.R2Foreign; -import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen; import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; @@ -254,7 +257,7 @@ final class REngine implements Engine, Engine.Timings { @Override public Object parseAndEval(Source source, MaterializedFrame frame, boolean printResult) throws ParseException { - List<RSyntaxNode> list = parseImpl(source); + List<RSyntaxNode> list = parseSource(source); try { Object lastValue = RNull.instance; for (RSyntaxNode node : list) { @@ -282,21 +285,23 @@ final class REngine implements Engine, Engine.Timings { } } - private List<RSyntaxNode> parseImpl(Source source) throws ParseException { + List<RSyntaxNode> parseSource(Source source) throws ParseException { RParserFactory.Parser<RSyntaxNode> parser = RParserFactory.getParser(); return parser.script(source, new RASTBuilder(), context.getLanguage()); } @Override public RExpression parse(Source source) throws ParseException { - List<RSyntaxNode> list = parseImpl(source); + List<RSyntaxNode> list = parseSource(source); Object[] data = list.stream().map(node -> RASTUtils.createLanguageElement(node)).toArray(); return RDataFactory.createExpression(data); } @Override public CallTarget parseToCallTarget(Source source, MaterializedFrame executionFrame) throws ParseException { - if (source == Engine.GET_CONTEXT) { + if (source.getPath() != null && !source.isInteractive()) { + return Truffle.getRuntime().createCallTarget(createRScriptRoot(source, executionFrame)); + } else if (source == Engine.GET_CONTEXT) { /* * The "get context" operations should be executed with as little influence on the * actual engine as possible, therefore this special case takes care of it explicitly. @@ -313,20 +318,21 @@ final class REngine implements Engine, Engine.Timings { } }); } else { - List<RSyntaxNode> statements = parseImpl(source); - return Truffle.getRuntime().createCallTarget(new PolyglotEngineRootNode(statements, createSourceSection(source, statements), executionFrame)); + List<RSyntaxNode> statements = parseSource(source); + EngineRootNode rootNode = EngineRootNode.createEngineRoot(this, context, statements, createSourceSection(source, statements), executionFrame); + return Truffle.getRuntime().createCallTarget(rootNode); } } @Override public ExecutableNode parseToExecutableNode(Source source) throws ParseException { - List<RSyntaxNode> list = parseImpl(source); + List<RSyntaxNode> list = parseSource(source); RNode[] statements = new RNode[list.size()]; for (int i = 0; i < statements.length; i++) { statements[i] = list.get(i).asRNode(); } return new ExecutableNode(context.getLanguage()) { - @Child R2Foreign toForeignNode = R2Foreign.create(); + @Child private R2Foreign toForeignNode = R2Foreign.create(); @Override public Object execute(VirtualFrame frame) { @@ -353,68 +359,49 @@ final class REngine implements Engine, Engine.Timings { } } - private final class PolyglotEngineRootNode extends RootNode { - - private final List<RSyntaxNode> statements; - private final MaterializedFrame executionFrame; - @Children private final DirectCallNode[] calls; - private final boolean printResult; - private final SourceSection sourceSection; - private final ContextReference<RContext> contextReference; - - @Child private R2Foreign r2Foreign = R2ForeignNodeGen.create(); - - PolyglotEngineRootNode(List<RSyntaxNode> statements, SourceSection sourceSection, MaterializedFrame executionFrame) { - super(context.getLanguage()); - this.sourceSection = sourceSection; - this.contextReference = context.getLanguage().getContextReference(); - // can't print if initializing the system in embedded mode (no builtins yet) - this.printResult = !sourceSection.getSource().getName().equals(RSource.Internal.INIT_EMBEDDED.string) && sourceSection.getSource().isInteractive(); - this.statements = statements; - this.executionFrame = executionFrame; - this.calls = new DirectCallNode[statements.size()]; - } - - @Override - public SourceSection getSourceSection() { - return sourceSection; - } - - /** - * The normal {@link #doMakeCallTarget} happens first, then we actually run the call using - * the standard FastR machinery, saving and restoring the {@link RContext}, since we have no - * control over what that might be when the call is initiated. - */ - @Override - @ExplodeLoop - public Object execute(VirtualFrame frame) { - Object actualFrame = executionFrame != null ? executionFrame : contextReference.get().stateREnvironment.getGlobalFrame(); - try { - Object lastValue = RNull.instance; - for (int i = 0; i < calls.length; i++) { - if (calls[i] == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - RSyntaxNode node = statements.get(i); - calls[i] = insert(Truffle.getRuntime().createDirectCallNode(doMakeCallTarget(node.asRNode(), RSource.Internal.REPL_WRAPPER.string, printResult, true))); + private EngineRootNode createRScriptRoot(Source fullSource, MaterializedFrame frame) { + URI uri = fullSource.getURI(); + String file = fullSource.getPath(); + ArrayList<RSyntaxNode> statements = new ArrayList<>(128); + try { + try (BufferedReader br = new BufferedReader(new InputStreamReader(fullSource.getInputStream()))) { + int lineIndex = 1; + int startLine = lineIndex; + StringBuilder sb = new StringBuilder(); + while (true) { + String input = br.readLine(); + if (input == null) { + if (sb.length() != 0) { + // end of file, but not end of statement => error + statements.add(new SyntaxErrorNode(null, fullSource.createSection(startLine, 1, sb.length()))); + } + break; + } + sb.append(input); + Source src = Source.newBuilder(sb.toString()).mimeType(RRuntime.R_APP_MIME).name(file + "#" + startLine + "-" + lineIndex).uri(uri).build(); + lineIndex++; + List<RSyntaxNode> currentStmts = null; + try { + RParserFactory.Parser<RSyntaxNode> parser = RParserFactory.getParser(); + currentStmts = parser.statements(src, fullSource, startLine, new RASTBuilder(), context.getLanguage()); + } catch (IncompleteSourceException e) { + sb.append('\n'); + continue; + } catch (ParseException e) { + statements.add(new SyntaxErrorNode(e, fullSource.createSection(startLine, 1, sb.length()))); } - lastValue = calls[i].call(new Object[]{actualFrame}); + if (currentStmts != null) { + statements.addAll(currentStmts); + } + // we did not continue on incomplete source exception + sb.setLength(0); + startLine = lineIndex; } - return r2Foreign.execute(lastValue); - } catch (ReturnException ex) { - return ex.getResult(); - } catch (DebugExitException | JumpToTopLevelException | ExitException | ThreadDeath e) { - CompilerDirectives.transferToInterpreter(); - throw e; - } catch (RError e) { - CompilerDirectives.transferToInterpreter(); - throw e; - } catch (Throwable t) { - CompilerDirectives.transferToInterpreter(); - // other errors didn't produce an output yet - RInternalError.reportError(t); - throw t; } + } catch (IOException ex) { + throw new RuntimeException(ex); } + return EngineRootNode.createEngineRoot(this, context, statements, createSourceSection(fullSource, statements), frame); } @Override @@ -508,7 +495,7 @@ final class REngine implements Engine, Engine.Timings { * that the body should be executed in. */ @TruffleBoundary - private RootCallTarget doMakeCallTarget(RNode body, String description, boolean printResult, boolean topLevel) { + RootCallTarget doMakeCallTarget(RNode body, String description, boolean printResult, boolean topLevel) { return Truffle.getRuntime().createCallTarget(new AnonymousRootNode(this, body, description, printResult, topLevel)); } @@ -529,14 +516,14 @@ final class REngine implements Engine, Engine.Timings { private final boolean topLevel; private final boolean suppressWarnings; - @Child private RNode body; + @Child private RootBodyNode body; @Child private GetVisibilityNode visibility = GetVisibilityNode.create(); @Child private SetVisibilityNode setVisibility = SetVisibilityNode.create(); protected AnonymousRootNode(REngine engine, RNode body, String description, boolean printResult, boolean topLevel) { super(engine.context.getLanguage()); this.suppressWarnings = engine.suppressWarnings; - this.body = body; + this.body = new AnonymousBodyNode(body); this.description = description; this.printResult = printResult; this.topLevel = topLevel; @@ -544,12 +531,12 @@ final class REngine implements Engine, Engine.Timings { @Override public SourceSection getSourceSection() { - return body.getSourceSection(); + return getBody().getSourceSection(); } @Override public boolean isInternal() { - return RSyntaxNode.isInternal(body.asRSyntaxNode().getLazySourceSection()); + return RSyntaxNode.isInternal(getBody().getLazySourceSection()); } private VirtualFrame prepareFrame(VirtualFrame frame) { @@ -620,6 +607,29 @@ final class REngine implements Engine, Engine.Timings { return false; } + @Override + public RSyntaxNode getBody() { + return body.getBody().asRSyntaxNode(); + } + } + + private static final class AnonymousBodyNode extends Node implements RootBodyNode { + @Child private RNode body; + + AnonymousBodyNode(RNode body) { + this.body = body; + } + + @Override + public Object visibleExecute(VirtualFrame frame) { + return body.visibleExecute(frame); + } + + @Override + public SourceSection getSourceSection() { + return body.getSourceSection(); + } + @Override public RNode getBody() { return body; diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java index 08cbe18cd4f7f2278adb4d28be3c838f9d233733..b4ee6e0c3a8e1ba383f49afd4a08604807b57992 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java @@ -33,6 +33,9 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.Frame; import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; +import com.oracle.truffle.api.instrumentation.StandardTags.CallTag; +import com.oracle.truffle.api.instrumentation.StandardTags.RootTag; +import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.r.launcher.RCommand; @@ -54,6 +57,8 @@ import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode; import com.oracle.truffle.r.nodes.function.RCallNode; import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation; +import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag; +import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.LoopTag; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; @@ -61,7 +66,7 @@ import com.oracle.truffle.r.runtime.RDeparse; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.ShowCallerOf; import com.oracle.truffle.r.runtime.RRuntimeASTAccess; -import com.oracle.truffle.r.runtime.RootWithBody; +import com.oracle.truffle.r.runtime.RootBodyNode; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.context.Engine; import com.oracle.truffle.r.runtime.context.RContext; @@ -265,45 +270,49 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { @Override public boolean isTaggedWith(Node node, Class<?> tag) { + if (node instanceof RootBodyNode) { + return (tag == RootTag.class); + } + if (node instanceof RootNode) { + // roots don't have any tags + return false; + } if (!(node instanceof RSyntaxNode)) { return false; } if (isInternalChild(node)) { return false; } - String className = tag.getSimpleName(); - switch (className) { - case "CallTag": - return node instanceof RCallNode; - - case "StatementTag": { - Node parent = ((RInstrumentableNode) node).unwrapParent(); - if (node instanceof BlockNode) { - // TODO we may reconsider this - return false; - } - // Most likely - if (parent instanceof BlockNode) { - return true; - } else { - // single statement block, variable parent - // note: RepeatingNode is not a RSyntaxElement but the body of a loop is - // under the repeating node ! - return parent instanceof FunctionDefinitionNode || parent instanceof RootWithBody || parent instanceof IfNode || AbstractLoopNode.isLoopBody(node); - } + if (tag == CallTag.class) { + return node instanceof RCallNode; + } + if (tag == FunctionBodyBlockTag.class) { + return node instanceof BlockNode && ((BlockNode) node).unwrapParent() instanceof RootBodyNode; + } + if (tag == LoopTag.class) { + return node instanceof AbstractLoopNode; + } + if (tag == StatementTag.class) { + if (node instanceof BlockNode) { + // so that the stepping location is not the block itself, but the first statement in + // the block, note that the FastR's own debugging and tracing mechanism uses + // FunctionBodyBlockTag to recognize function bodies. + return false; } - - case "RootTag": { - Node parent = ((RInstrumentableNode) node).unwrapParent(); - return parent instanceof FunctionDefinitionNode || parent instanceof RootWithBody; + // How to recognize statement from some node inside a statement (e.g. expression)? + Node parent = ((RInstrumentableNode) node).unwrapParent(); + if (parent instanceof BlockNode) { + // It's in a block of statements + return true; + } else { + // single statement block: as function body, if/else body, loop body + // note: RepeatingNode is not a RSyntaxElement but the body of a loop is + // under the repeating node ! + return parent instanceof RootBodyNode || parent instanceof IfNode || AbstractLoopNode.isLoopBody(node) || EngineRootNode.isEngineBody(parent); } - - case "LoopTag": - return node instanceof AbstractLoopNode; - - default: - return false; } + // TODO: ExpressionTag: (!statement && !loop && !if && !call && !root)?? + return false; } private static boolean isInternalChild(Node node) { diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java new file mode 100644 index 0000000000000000000000000000000000000000..10ecf7472e6ff21daa88ceba29822e7140178c0b --- /dev/null +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/SyntaxErrorNode.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2018, 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.r.engine; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; +import com.oracle.truffle.r.runtime.nodes.RInstrumentableNode; +import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; + +/** + * Node that represents piece of AST that would throw syntax error when parser. Used to delay + * parsing error to the point when they should be reported. + */ +final class SyntaxErrorNode extends RNode implements RSyntaxNode, RInstrumentableNode { + private final ParseException exception; + private final SourceSection sourceSection; + + SyntaxErrorNode(ParseException exception, SourceSection sourceSection) { + this.exception = exception; + this.sourceSection = sourceSection; + } + + @Override + public Object execute(VirtualFrame frame) { + throw exception.throwAsRError(); + } + + @Override + public SourceSection getSourceSection() { + return this.sourceSection; + } + + @Override + public SourceSection getLazySourceSection() { + return this.sourceSection; + } + + @Override + public void setSourceSection(SourceSection source) { + throw RInternalError.shouldNotReachHere(String.format("%s should not be serialized/deserialzed.", getClass().getSimpleName())); + } +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java index e84096fba53c49263117a1cf1e4ec25ff356c8b1..50633e95daf86e2f7f48ce313b85859912660432 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguageImpl.java @@ -34,11 +34,13 @@ import com.oracle.truffle.api.instrumentation.ProvidedTags; import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.nodes.ExecutableNode; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.r.engine.interop.RForeignAccessFactoryImpl; import com.oracle.truffle.r.nodes.RASTBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinPackages; import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags; +import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag; import com.oracle.truffle.r.runtime.ExitException; import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RAccuracyInfo; @@ -57,7 +59,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory; import com.oracle.truffle.r.runtime.nodes.RBaseNode; @TruffleLanguage.Registration(name = "R", id = "R", version = "3.3.2", mimeType = {RRuntime.R_APP_MIME, RRuntime.R_TEXT_MIME}, interactive = true) -@ProvidedTags({StandardTags.CallTag.class, StandardTags.StatementTag.class, StandardTags.RootTag.class, RSyntaxTags.LoopTag.class}) +@ProvidedTags({StandardTags.CallTag.class, StandardTags.StatementTag.class, StandardTags.RootTag.class, RSyntaxTags.LoopTag.class, FunctionBodyBlockTag.class}) public final class TruffleRLanguageImpl extends TruffleRLanguage { private final HashMap<String, RFunction> builtinFunctionCache = new HashMap<>(); @@ -188,12 +190,13 @@ public final class TruffleRLanguageImpl extends TruffleRLanguage { @Override protected CallTarget parse(ParsingRequest request) throws Exception { CompilerAsserts.neverPartOfCompilation(); + Source source = request.getSource(); try { - return RContext.getEngine().parseToCallTarget(request.getSource(), null); + return RContext.getEngine().parseToCallTarget(source, null); } catch (IncompleteSourceException e) { throw e; } catch (ParseException e) { - if (request.getSource().isInteractive()) { + if (source.isInteractive()) { throw e.throwAsRError(); } else { throw e; diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java deleted file mode 100644 index 6e73843f7b4bea548187d329a4859a74ef0979fc..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/Launcher.java +++ /dev/null @@ -1,503 +0,0 @@ -/* - * Copyright (c) 2017, 2018, 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.r.launcher; - -import static java.lang.Integer.max; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.graalvm.options.OptionCategory; -import org.graalvm.options.OptionDescriptor; -import org.graalvm.options.OptionDescriptors; -import org.graalvm.polyglot.Engine; -import org.graalvm.polyglot.Instrument; -import org.graalvm.polyglot.Language; - -public abstract class Launcher { - private static final boolean STATIC_VERBOSE = Boolean.getBoolean("org.graalvm.launcher.verbose"); - - private Engine tempEngine; - - private final boolean verbose; - - private boolean help; - private boolean helpDebug; - private boolean helpExpert; - private boolean helpTools; - private boolean helpLanguages; - - private OptionCategory helpCategory; - private VersionAction versionAction = VersionAction.None; - - protected enum VersionAction { - None, - PrintAndExit, - PrintAndContinue - } - - public Launcher() { - verbose = STATIC_VERBOSE || Boolean.valueOf(System.getenv("VERBOSE_GRAALVM_LAUNCHERS")); - } - - private Engine getTempEngine() { - if (tempEngine == null) { - tempEngine = Engine.create(); - } - return tempEngine; - } - - void setHelpCategory(OptionCategory helpCategory) { - this.helpCategory = helpCategory; - } - - protected void setVersionAction(VersionAction versionAction) { - this.versionAction = versionAction; - } - - private static class AbortException extends RuntimeException { - static final long serialVersionUID = 4681646279864737876L; - - AbortException(String message) { - super(message, null); - } - - @SuppressWarnings("sync-override") - @Override - public final Throwable fillInStackTrace() { - return null; - } - } - - protected AbortException exit() { - return abort(null, 0); - } - - protected AbortException abort(String message) { - return abort(message, 1); - } - - protected AbortException abort(String message, int exitCode) { - if (message != null) { - System.err.println("ERROR: " + message); - } - System.exit(exitCode); - throw new AbortException(message); - } - - protected abstract void printHelp(OptionCategory maxCategory); - - protected abstract void printVersion(); - - protected abstract void collectArguments(Set<String> options); - - protected static void printPolyglotVersions() { - Engine engine = Engine.create(); - System.out.println("GraalVM Polyglot Engine Version " + engine.getVersion()); - printLanguages(engine, true); - printInstruments(engine, true); - } - - protected boolean isVerbose() { - return verbose; - } - - final boolean runPolyglotAction() { - OptionCategory maxCategory = helpDebug ? OptionCategory.DEBUG : (helpExpert ? OptionCategory.EXPERT : (helpCategory != null ? helpCategory : OptionCategory.USER)); - - switch (versionAction) { - case PrintAndContinue: - printVersion(); - return false; - case PrintAndExit: - printVersion(); - return true; - case None: - break; - } - boolean printDefaultHelp = helpCategory != null || help || ((helpExpert || helpDebug) && !helpTools && !helpLanguages); - Engine tmpEngine = null; - if (printDefaultHelp) { - printHelp(maxCategory); - // @formatter:off - System.out.println(); - System.out.println("Runtime Options:"); - printOption("--polyglot", "Run with all other guest languages accessible."); - // not applicable at the moment - // printOption("--native", "Run using the native launcher with limited Java access (default)."); - // printOption("--native.[option]", "Pass options to the native image; for example, '--native.Xmx1G'. To see available options, use '--native.help'."); - printOption("--jvm", "Run on the Java Virtual Machine with Java access."); - // not applicable at the moment - // printOption("--jvm.[option]", "Pass options to the JVM; for example, '--jvm.classpath=myapp.jar'. To see available options. use '--jvm.help'."); - printOption("--help", "Print this help message."); - printOption("--help:languages", "Print options for all installed languages."); - printOption("--help:tools", "Print options for all installed tools."); - printOption("--help:expert", "Print additional engine options for experts."); - if (helpExpert || helpDebug) { - printOption("--help:debug", "Print additional engine options for debugging."); - } - printOption("--version", "Print version information and exit."); - printOption("--show-version", "Print version information and continue execution."); - // @formatter:on - if (tmpEngine == null) { - tmpEngine = Engine.create(); - } - List<PrintableOption> engineOptions = new ArrayList<>(); - for (OptionDescriptor descriptor : tmpEngine.getOptions()) { - if (!descriptor.getName().startsWith("engine.") && !descriptor.getName().startsWith("compiler.")) { - continue; - } - if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) { - engineOptions.add(asPrintableOption(descriptor)); - } - } - if (!engineOptions.isEmpty()) { - printOptions(engineOptions, "Engine options:", 2); - } - } - - if (helpLanguages) { - if (tmpEngine == null) { - tmpEngine = Engine.create(); - } - printLanguageOptions(tmpEngine, maxCategory); - } - - if (helpTools) { - if (tmpEngine == null) { - tmpEngine = Engine.create(); - } - printInstrumentOptions(tmpEngine, maxCategory); - } - - if (printDefaultHelp || helpLanguages || helpTools) { - System.out.println("\nSee http://www.oracle.com/technetwork/oracle-labs/program-languages/overview/index.html for more information."); - return true; - } - - return false; - } - - private static void printInstrumentOptions(Engine engine, OptionCategory maxCategory) { - Map<Instrument, List<PrintableOption>> instrumentsOptions = new HashMap<>(); - List<Instrument> instruments = sortedInstruments(engine); - for (Instrument instrument : instruments) { - List<PrintableOption> options = new ArrayList<>(); - for (OptionDescriptor descriptor : instrument.getOptions()) { - if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) { - options.add(asPrintableOption(descriptor)); - } - } - if (!options.isEmpty()) { - instrumentsOptions.put(instrument, options); - } - } - if (!instrumentsOptions.isEmpty()) { - System.out.println(); - System.out.println("Tool options:"); - for (Instrument instrument : instruments) { - List<PrintableOption> options = instrumentsOptions.get(instrument); - if (options != null) { - printOptions(options, " " + instrument.getName() + ":", 4); - } - } - } - } - - private static void printLanguageOptions(Engine engine, OptionCategory maxCategory) { - Map<Language, List<PrintableOption>> languagesOptions = new HashMap<>(); - List<Language> languages = sortedLanguages(engine); - for (Language language : languages) { - List<PrintableOption> options = new ArrayList<>(); - for (OptionDescriptor descriptor : language.getOptions()) { - if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) { - options.add(asPrintableOption(descriptor)); - } - } - if (!options.isEmpty()) { - languagesOptions.put(language, options); - } - } - if (!languagesOptions.isEmpty()) { - System.out.println(); - System.out.println("Language Options:"); - for (Language language : languages) { - List<PrintableOption> options = languagesOptions.get(language); - if (options != null) { - printOptions(options, " " + language.getName() + ":", 4); - } - } - } - } - - protected boolean parsePolyglotOption(String defaultOptionPrefix, Map<String, String> options, String arg) { - switch (arg) { - case "--help": - help = true; - return true; - case "--help:debug": - helpDebug = true; - return true; - case "--help:expert": - helpExpert = true; - return true; - case "--help:tools": - helpTools = true; - return true; - case "--help:languages": - helpLanguages = true; - return true; - case "--version": - versionAction = VersionAction.PrintAndExit; - return true; - case "--show-version": - versionAction = VersionAction.PrintAndContinue; - return true; - // case "--polyglot": - // case "--jvm": - // case "--native": - // return false; - default: - // getLanguageId() or null? - if (arg.length() <= 2 || !arg.startsWith("--")) { - return false; - } - int eqIdx = arg.indexOf('='); - String key; - String value; - if (eqIdx < 0) { - key = arg.substring(2); - value = null; - } else { - key = arg.substring(2, eqIdx); - value = arg.substring(eqIdx + 1); - } - - if (value == null) { - value = "true"; - } - int index = key.indexOf('.'); - String group = key; - if (index >= 0) { - group = group.substring(0, index); - } - OptionDescriptor descriptor = findPolyglotOptionDescriptor(group, key); - if (descriptor == null) { - if (defaultOptionPrefix != null) { - descriptor = findPolyglotOptionDescriptor(defaultOptionPrefix, defaultOptionPrefix + "." + key); - } - if (descriptor == null) { - return false; - } - } - try { - descriptor.getKey().getType().convert(value); - } catch (IllegalArgumentException e) { - throw abort(String.format("Invalid argument %s specified. %s'", arg, e.getMessage())); - } - options.put(key, value); - return true; - } - } - - private OptionDescriptor findPolyglotOptionDescriptor(String group, String key) { - OptionDescriptors descriptors = null; - switch (group) { - case "compiler": - case "engine": - descriptors = getTempEngine().getOptions(); - break; - default: - Engine engine = getTempEngine(); - if (engine.getLanguages().containsKey(group)) { - descriptors = engine.getLanguages().get(group).getOptions(); - } else if (engine.getInstruments().containsKey(group)) { - descriptors = engine.getInstruments().get(group).getOptions(); - } - break; - } - if (descriptors == null) { - return null; - } - return descriptors.get(key); - } - - protected static List<Language> sortedLanguages(Engine engine) { - List<Language> languages = new ArrayList<>(engine.getLanguages().values()); - languages.sort(Comparator.comparing(Language::getId)); - return languages; - } - - protected static List<Instrument> sortedInstruments(Engine engine) { - List<Instrument> instruments = new ArrayList<>(engine.getInstruments().values()); - instruments.sort(Comparator.comparing(Instrument::getId)); - return instruments; - } - - protected static void printOption(OptionCategory maxCategory, OptionDescriptor descriptor) { - if (descriptor.getCategory().ordinal() <= maxCategory.ordinal()) { - printOption(asPrintableOption(descriptor)); - } - } - - protected static PrintableOption asPrintableOption(OptionDescriptor descriptor) { - StringBuilder key = new StringBuilder("--"); - key.append(descriptor.getName()); - Object defaultValue = descriptor.getKey().getDefaultValue(); - if (defaultValue instanceof Boolean && defaultValue == Boolean.FALSE) { - // nothing to print - } else { - key.append("=<"); - key.append(descriptor.getKey().getType().getName()); - key.append(">"); - } - return new PrintableOption(key.toString(), descriptor.getHelp()); - } - - protected static void printOption(String option, String description) { - printOption(option, description, 2); - } - - protected static void printOption(String option, String description, int indentation) { - StringBuilder indent = new StringBuilder(indentation); - for (int i = 0; i < indentation; i++) { - indent.append(' '); - } - String desc = description != null ? description : ""; - if (option.length() >= 45 && description != null) { - System.out.println(String.format("%s%s%n%s%-45s%s", indent, option, indent, "", desc)); - } else { - System.out.println(String.format("%s%-45s%s", indent, option, desc)); - } - } - - protected static void printOption(PrintableOption option) { - printOption(option, 2); - } - - protected static void printOption(PrintableOption option, int indentation) { - printOption(option.option, option.description, indentation); - } - - private static final class PrintableOption implements Comparable<PrintableOption> { - final String option; - final String description; - - private PrintableOption(String option, String description) { - this.option = option; - this.description = description; - } - - @Override - public int compareTo(PrintableOption o) { - return this.option.compareTo(o.option); - } - } - - private static void printOptions(List<PrintableOption> options, String title, int indentation) { - Collections.sort(options); - System.out.println(title); - for (PrintableOption option : options) { - printOption(option, indentation); - } - } - - protected enum OS { - Darwin, - Linux, - Solaris; - - private static OS findCurrent() { - final String name = System.getProperty("os.name"); - if (name.equals("Linux")) { - return Linux; - } - if (name.equals("SunOS")) { - return Solaris; - } - if (name.equals("Mac OS X") || name.equals("Darwin")) { - return Darwin; - } - throw new IllegalArgumentException("unknown OS: " + name); - } - - private static final OS current = findCurrent(); - - public static OS getCurrent() { - return current; - } - } - - protected static void printLanguages(Engine engine, boolean printWhenEmpty) { - if (engine.getLanguages().isEmpty()) { - if (printWhenEmpty) { - System.out.println(" Installed Languages: none"); - } - } else { - System.out.println(" Installed Languages:"); - List<Language> languages = new ArrayList<>(engine.getLanguages().size()); - int nameLength = 0; - for (Language language : engine.getLanguages().values()) { - languages.add(language); - nameLength = max(nameLength, language.getName().length()); - } - languages.sort(Comparator.comparing(Language::getId)); - String langFormat = " %-" + nameLength + "s%s version %s%n"; - for (Language language : languages) { - String version = language.getVersion(); - if (version == null || version.length() == 0) { - version = ""; - } - System.out.printf(langFormat, language.getName().isEmpty() ? "Unnamed" : language.getName(), version); - } - } - } - - protected static void printInstruments(Engine engine, boolean printWhenEmpty) { - if (engine.getInstruments().isEmpty()) { - if (printWhenEmpty) { - System.out.println(" Installed Tools: none"); - } - } else { - System.out.println(" Installed Tools:"); - List<Instrument> instruments = sortedInstruments(engine); - int nameLength = 0; - for (Instrument instrument : instruments) { - nameLength = max(nameLength, instrument.getName().length()); - } - String instrumentFormat = " %-" + nameLength + "s version %s%n"; - for (Instrument instrument : instruments) { - String version = instrument.getVersion(); - if (version == null || version.length() == 0) { - version = ""; - } - System.out.printf(instrumentFormat, instrument.getName().isEmpty() ? "Unnamed" : instrument.getName(), version); - } - } - } -} diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RAbstractLauncher.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RAbstractLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..87fa2f782a004ac798a80ee58f034c7f0cbd388f --- /dev/null +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RAbstractLauncher.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018, 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.r.launcher; + +import java.io.Closeable; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.graalvm.launcher.AbstractLanguageLauncher; +import org.graalvm.options.OptionCategory; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Context.Builder; + +import com.oracle.truffle.r.launcher.RCmdOptions.Client; +import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption; + +public abstract class RAbstractLauncher extends AbstractLanguageLauncher implements Closeable { + + private final Client client; + protected final InputStream inStream; + protected final OutputStream outStream; + protected final OutputStream errStream; + protected RCmdOptions options; + private boolean useJVM; + protected ConsoleHandler consoleHandler; + protected Context context; + + RAbstractLauncher(Client client, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { + this.client = client; + assert env == null : "re-enble environment variables"; + this.inStream = inStream; + this.outStream = outStream; + this.errStream = errStream; + } + + @Override + protected List<String> preprocessArguments(List<String> arguments, Map<String, String> polyglotOptions) { + boolean[] recognizedArgsIndices = new boolean[arguments.size()]; + this.options = RCmdOptions.parseArguments(client, arguments.toArray(new String[arguments.size()]), true, recognizedArgsIndices); + List<String> unrecognizedArgs = new ArrayList<>(); + for (int i = 0; i < arguments.size(); i++) { + if ("--jvm".equals(arguments.get(i))) { + useJVM = true; + } else if (!recognizedArgsIndices[i]) { + unrecognizedArgs.add(arguments.get(i)); + } + } + + return unrecognizedArgs; + } + + protected abstract String[] getArguments(); + + @Override + protected void launch(Builder contextBuilder) { + this.consoleHandler = RCommand.createConsoleHandler(options, null, inStream, outStream); + this.context = contextBuilder.allowAllAccess(true).allowHostAccess(useJVM).arguments("R", getArguments()).in(consoleHandler.createInputStream()).out( + outStream).err(errStream).build(); + this.consoleHandler.setContext(context); + } + + @Override + protected String getLanguageId() { + return "R"; + } + + @Override + protected void printHelp(OptionCategory maxCategory) { + RCmdOptions.printHelp(client); + } + + @Override + protected void collectArguments(Set<String> opts) { + for (RCmdOption option : RCmdOption.values()) { + if (option.shortName != null) { + opts.add(option.shortName); + } + if (option.plainName != null) { + opts.add(option.plainName); + } + } + } + + @Override + public void close() { + if (context != null) { + context.close(); + } + } +} diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java index f402d46bd91411a36d0dc25b14d08bc825acf36b..d0cb3e769d91cdb1e8247b2779919f8775cb31d8 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCmdOptions.java @@ -268,25 +268,44 @@ public final class RCmdOptions { * {@code args[0]} and we do not want to parse that! */ public static RCmdOptions parseArguments(Client client, String[] args, boolean reparse) { + return parseArguments(client, args, reparse, null); + } + + public static RCmdOptions parseArguments(Client client, String[] args, boolean reparse, boolean[] recognizedArgsIndices) { + assert recognizedArgsIndices == null || recognizedArgsIndices.length == args.length; + EnumMap<RCmdOption, Object> options = new EnumMap<>(RCmdOption.class); + if (recognizedArgsIndices != null) { + recognizedArgsIndices[0] = true; + } int i = 1; // skip the first argument (the command) int firstNonOptionArgIndex = args.length; while (i < args.length) { final String arg = args[i]; MatchResult result = matchOption(arg); if (result == null) { - // for Rscript, this means we are done - if (client == Client.RSCRIPT) { + boolean isOption = arg.startsWith("--") || arg.startsWith("-"); + if (!isOption && client == Client.RSCRIPT) { + // for Rscript, this means we are done + if (recognizedArgsIndices != null) { + recognizedArgsIndices[i] = true; + } firstNonOptionArgIndex = i; break; } if (!reparse) { // GnuR does not abort, simply issues a warning System.out.printf("WARNING: unknown option '%s'%n", arg); + if (recognizedArgsIndices != null) { + recognizedArgsIndices[i] = true; + } } i++; continue; } else { + if (recognizedArgsIndices != null) { + recognizedArgsIndices[i] = true; + } RCmdOption option = result.option; if (result.matchedShort && i == args.length - 1) { System.out.println("usage:"); @@ -302,6 +321,9 @@ public final class RCmdOptions { if (result.matchedShort) { i++; setValue(options, option, args[i]); + if (recognizedArgsIndices != null) { + recognizedArgsIndices[i] = true; + } } else { if (option.type == RCmdOptionType.BOOLEAN) { setValue(options, option, true); @@ -318,6 +340,14 @@ public final class RCmdOptions { } } } + + if (recognizedArgsIndices != null) { + // mark the all non-option arguments (the tail) as recognized + for (int j = firstNonOptionArgIndex; j < recognizedArgsIndices.length; j++) { + recognizedArgsIndices[j] = true; + } + } + // adjust for inserted executable name return new RCmdOptions(options, args, firstNonOptionArgIndex); } diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java index 1a1fdc1f067af61e4118964bba654dcfcbcfc698..3b9e23dc93c426900cb0beb1e03f77b80d0e5e98 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RCommand.java @@ -30,15 +30,11 @@ import java.io.OutputStream; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.function.Supplier; -import org.graalvm.options.OptionCategory; import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Context.Builder; import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.Source; @@ -47,37 +43,6 @@ import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption; import jline.console.UserInterruptException; -class RLauncher extends Launcher { - - private final Client client; - - RLauncher(Client client) { - this.client = client; - } - - @Override - protected void printHelp(OptionCategory maxCategory) { - RCmdOptions.printHelp(client); - } - - @Override - protected void printVersion() { - RCmdOptions.printVersion(); - } - - @Override - protected void collectArguments(Set<String> options) { - for (RCmdOption option : RCmdOption.values()) { - if (option.shortName != null) { - options.add(option.shortName); - } - if (option.plainName != null) { - options.add(option.plainName); - } - } - } -} - /* * TODO: * @@ -89,40 +54,25 @@ class RLauncher extends Launcher { /** * Emulates the (Gnu)R command as precisely as possible. */ -public class RCommand { - - // CheckStyle: stop system..print check - public static RuntimeException fatal(String message, Object... args) { - System.out.println("FATAL: " + String.format(message, args)); - System.exit(-1); - return new RuntimeException(); - } +public class RCommand extends RAbstractLauncher { - public static RuntimeException fatal(Throwable t, String message, Object... args) { - t.printStackTrace(); - System.out.println("FATAL: " + String.format(message, args)); - System.exit(-1); - return null; + RCommand(String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { + super(Client.R, env, inStream, outStream, errStream); } - public static void main(String[] args) { - try { - System.exit(doMain(prependCommand(args), null, System.in, System.out, System.err)); - // never returns - throw fatal("main should never return"); - } catch (Throwable t) { - throw fatal(t, "error during REPL execution"); - } + @Override + protected String[] getArguments() { + return options.getArguments(); } - static String[] prependCommand(String[] args) { - String[] result = new String[args.length + 1]; - result[0] = "R"; - System.arraycopy(args, 0, result, 1, args.length); - return result; + @Override + protected void launch(Builder contextBuilder) { + super.launch(contextBuilder); + StartupTiming.timestamp("VM Created"); + StartupTiming.printSummary(); } - public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { + private int execute(String[] args) { StartupTiming.timestamp("Main Entered"); ArrayList<String> argsList = new ArrayList<>(Arrays.asList(args)); if (System.console() != null) { @@ -143,34 +93,8 @@ public class RCommand { } } - boolean useJVM = false; - RLauncher launcher = new RLauncher(Client.R); - Map<String, String> polyglotOptions = new HashMap<>(); - Iterator<String> iterator = argsList.iterator(); - if (iterator.hasNext()) { - iterator.next(); // skip first argument - while (iterator.hasNext()) { - String arg = iterator.next(); - if ("--jvm".equals(arg)) { - useJVM = true; - iterator.remove(); - } else if (launcher.parsePolyglotOption("R", polyglotOptions, arg)) { - iterator.remove(); - } - } - } - if (launcher.runPolyglotAction()) { - return 0; - } - RCmdOptions options = RCmdOptions.parseArguments(Client.R, argsList.toArray(new String[argsList.size()]), false); - assert env == null || env.length == 0 : "re-enable setting environments"; - ConsoleHandler consoleHandler = createConsoleHandler(options, null, inStream, outStream); - try (Context context = Context.newBuilder().allowAllAccess(true).allowHostAccess(useJVM).options(polyglotOptions).arguments("R", options.getArguments()).in( - consoleHandler.createInputStream()).out(outStream).err(errStream).build()) { - consoleHandler.setContext(context); - StartupTiming.timestamp("VM Created"); - StartupTiming.printSummary(); - + launch(argsList.toArray(new String[0])); + if (context != null) { File srcFile = null; String fileOption = options.getString(RCmdOption.FILE); if (fileOption != null) { @@ -178,6 +102,45 @@ public class RCommand { } return readEvalPrint(context, consoleHandler, srcFile); + } else { + return 0; + } + } + + // CheckStyle: stop system..print check + public static RuntimeException fatal(String message, Object... args) { + System.out.println("FATAL: " + String.format(message, args)); + System.exit(-1); + return new RuntimeException(); + } + + public static RuntimeException fatal(Throwable t, String message, Object... args) { + t.printStackTrace(); + System.out.println("FATAL: " + String.format(message, args)); + System.exit(-1); + return null; + } + + static String[] prependCommand(String[] args) { + String[] result = new String[args.length + 1]; + result[0] = "R"; + System.arraycopy(args, 0, result, 1, args.length); + return result; + } + + public static void main(String[] args) { + try { + System.exit(doMain(prependCommand(args), null, System.in, System.out, System.err)); + // never returns + throw fatal("main should never return"); + } catch (Throwable t) { + throw fatal(t, "error during REPL execution"); + } + } + + public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { + try (RCommand rcmd = new RCommand(env, inStream, outStream, errStream)) { + return rcmd.execute(args); } } diff --git a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java index 15c4be2c6404f3332c001d5db43c0e262e069cb5..99ce2d589b25af2bc8de33625b1ec6e7fe876249 100644 --- a/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java +++ b/com.oracle.truffle.r.launcher/src/com/oracle/truffle/r/launcher/RscriptCommand.java @@ -23,16 +23,16 @@ package com.oracle.truffle.r.launcher; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; +import java.util.Collections; +import java.util.List; import java.util.Map; -import org.graalvm.options.OptionCategory; -import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.PolyglotException; +import org.graalvm.polyglot.Source; import com.oracle.truffle.r.launcher.RCmdOptions.Client; import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption; @@ -43,11 +43,67 @@ import com.oracle.truffle.r.launcher.RCmdOptions.RCmdOption; * way but the effect is similar. * */ -public class RscriptCommand { +public final class RscriptCommand extends RAbstractLauncher { + + private String[] rScriptArguments; + + RscriptCommand(String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { + super(Client.RSCRIPT, env, inStream, outStream, errStream); + } + + @Override + protected List<String> preprocessArguments(List<String> arguments, Map<String, String> polyglotOptions) { + List<String> unrecognizedArgs = super.preprocessArguments(arguments, polyglotOptions); + try { + this.rScriptArguments = preprocessRScriptOptions(options); + return unrecognizedArgs; + } catch (PrintHelp e) { + return Collections.singletonList("--help"); + } + } + + @Override + protected String[] getArguments() { + return rScriptArguments; + } + + protected int execute(String[] args) { + launch(args); + if (context != null) { + String fileOption = options.getString(RCmdOption.FILE); + if (fileOption != null) { + return executeFile(fileOption); + } else { + return RCommand.readEvalPrint(context, consoleHandler, null); + } + } else { + return 0; + } + } + + private int executeFile(String fileOption) { + Source src; + try { + src = Source.newBuilder("R", new File(fileOption)).interactive(false).build(); + } catch (IOException ex) { + System.err.printf("IO error while reading the source file '%s'.\nDetails: '%s'.", fileOption, ex.getLocalizedMessage()); + return 1; + } + try { + context.eval(src); + return 0; + } catch (Throwable ex) { + if (ex instanceof PolyglotException && ((PolyglotException) ex).isExit()) { + return ((PolyglotException) ex).getExitStatus(); + } + // Internal exceptions are reported by the engine already + return 1; + } + } // CheckStyle: stop system..print check - private static String[] preprocessRScriptOptions(RLauncher launcher, RCmdOptions options) { + private static String[] preprocessRScriptOptions(RCmdOptions options) throws PrintHelp { String[] arguments = options.getArguments(); int resultArgsLength = arguments.length; int firstNonOptionArgIndex = options.getFirstNonOptionArgIndex(); @@ -61,11 +117,7 @@ public class RscriptCommand { // Either -e options are set or first non-option arg is a file if (options.getStringList(RCmdOption.EXPR) == null) { if (firstNonOptionArgIndex == resultArgsLength) { - launcher.setHelpCategory(OptionCategory.USER); - // does not return - if (launcher.runPolyglotAction()) { - System.exit(1); - } + throw new PrintHelp(); } else { options.setValue(RCmdOption.FILE, arguments[firstNonOptionArgIndex]); } @@ -99,50 +151,18 @@ public class RscriptCommand { return adjArgs.toArray(new String[adjArgs.size()]); } + @SuppressWarnings("serial") + static class PrintHelp extends Exception { + } + public static void main(String[] args) { System.exit(doMain(RCommand.prependCommand(args), null, System.in, System.out, System.err)); throw RCommand.fatal("should not reach here"); } public static int doMain(String[] args, String[] env, InputStream inStream, OutputStream outStream, OutputStream errStream) { - assert env == null : "re-enble environment variables"; - - ArrayList<String> argsList = new ArrayList<>(Arrays.asList(args)); - RLauncher launcher = new RLauncher(Client.RSCRIPT) { - @Override - protected void printVersion() { - System.out.print("R scripting front-end version "); - System.out.print(RVersionNumber.FULL); - System.out.println(RVersionNumber.RELEASE_DATE); - } - }; - boolean useJVM = false; - Map<String, String> polyglotOptions = new HashMap<>(); - Iterator<String> iterator = argsList.iterator(); - if (iterator.hasNext()) { - iterator.next(); // skip first argument - while (iterator.hasNext()) { - String arg = iterator.next(); - if ("--jvm".equals(arg)) { - useJVM = true; - iterator.remove(); - } else if (launcher.parsePolyglotOption("R", polyglotOptions, arg)) { - iterator.remove(); - } - } - } - if (launcher.runPolyglotAction()) { - return 0; - } - RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, argsList.toArray(new String[argsList.size()]), false); - String[] arguments = preprocessRScriptOptions(launcher, options); - - ConsoleHandler consoleHandler = RCommand.createConsoleHandler(options, null, inStream, outStream); - try (Context context = Context.newBuilder().allowAllAccess(true).allowHostAccess(useJVM).options(polyglotOptions).arguments("R", arguments).in(consoleHandler.createInputStream()).out( - outStream).err(errStream).build()) { - consoleHandler.setContext(context); - String fileOption = options.getString(RCmdOption.FILE); - return RCommand.readEvalPrint(context, consoleHandler, fileOption != null ? new File(fileOption) : null); + try (RscriptCommand rcmd = new RscriptCommand(env, inStream, outStream, errStream)) { + return rcmd.execute(args); } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java index 70bda866deda77c3008f5519f9c85339645824ce..e5d0ee8f424911c7c569c1541388d5e13ff87076 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java @@ -46,6 +46,7 @@ import com.oracle.truffle.r.nodes.control.AbstractLoopNode; import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode; import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation; import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags; +import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag; import com.oracle.truffle.r.runtime.JumpToTopLevelException; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RDeparse; @@ -76,7 +77,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor; * Three different listener classes are defined: * <ul> * <li>{@link FunctionStatementsEventListener}: attaches to function bodies and handles the special - * behavior on entry/exit</li> + * behavior on entry/exit</li>. Function body is distinguished with tag {@link FunctionBodyBlockTag} * <li>{@link StatementEventListener}: attaches to all {@code StandardTags.StatementTag} nodes and * handles "n" and "s" browser commands</li> * <li>{@link LoopStatementEventListener}: attaches to {@link AbstractLoopNode} instances and @@ -89,7 +90,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor; * mode and reset it's state on return. This is handled as follows: * <ol> * <li>On a step-into, attach a {@link StepIntoInstrumentListener} with a filter that matches all - * functions and the {@code StandardTags.RootTag} tag</li> + * functions and their bodies like {@link FunctionStatementsEventListener}.</li> * <li>On entry to that listener instrument/enable the function we have entered (if necessary) for * one-time (unless already)</li> * <li>Dispose the {@link StepIntoInstrumentListener} and continue, which will then stop at the @@ -219,7 +220,7 @@ public class DebugHandling { } @CompilationFinal private boolean disabled; - CyclicAssumption disabledUnchangedAssumption = new CyclicAssumption("debug event disabled state unchanged"); + private final CyclicAssumption disabledUnchangedAssumption = new CyclicAssumption("debug event disabled state unchanged"); boolean disabled() { return disabled || RContext.getInstance().stateInstrumentation.debugGloballyDisabled(); @@ -317,7 +318,7 @@ public class DebugHandling { @TruffleBoundary private void attachStepInto() { FunctionStatementsEventListener parentListener = getFunctionStatementsEventListener(functionDefinitionNode); - parentListener.stepIntoInstrument = RInstrumentation.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.RootTag.class).build(), + parentListener.stepIntoInstrument = RInstrumentation.getInstrumenter().attachExecutionEventListener(SourceSectionFilter.newBuilder().tagIs(FunctionBodyBlockTag.class).build(), new StepIntoInstrumentListener(parentListener)); } @@ -430,8 +431,10 @@ public class DebugHandling { public void attach() { + // Note: BlockStatement is not tagged as a STATEMENT, but there is FastR specific + // FunctionBodyBlockTag Instrumenter instrumenter = RInstrumentation.getInstrumenter(); - SourceSectionFilter.Builder functionBuilder = RInstrumentation.createFunctionFilter(functionDefinitionNode, StandardTags.RootTag.class); + SourceSectionFilter.Builder functionBuilder = RInstrumentation.createFunctionFilter(functionDefinitionNode, FunctionBodyBlockTag.class); setBinding(instrumenter.attachExecutionEventListener(functionBuilder.build(), this)); // Next attach statement handler to all STATEMENTs except LOOPs @@ -470,7 +473,7 @@ public class DebugHandling { accept(element.getSyntaxBody()); return null; } - }.accept(functionDefinitionNode); + }.accept(functionDefinitionNode.getBody()); } @Override @@ -524,8 +527,9 @@ public class DebugHandling { CompilerDirectives.transferToInterpreter(); print("debugging in: ", false); printCall(frame); - printNode(context.getInstrumentedNode(), true); - browserInteract(context.getInstrumentedNode(), frame); + Node node = context.getInstrumentedNode(); + printNode(node, true); + browserInteract(node, frame); } @Override @@ -582,6 +586,7 @@ public class DebugHandling { String callString = RContext.getRRuntimeASTAccess().getCallerSource(RArguments.getCall(frame)); print(callString, true); } + } @TruffleBoundary @@ -621,10 +626,7 @@ public class DebugHandling { // in case we did a step into that never called a function clearStepInstrument(); RBaseNode node = (RBaseNode) context.getInstrumentedNode(); - if (node.hasTag(StandardTags.RootTag.class)) { - // already handled - return; - } + assert !node.hasTag(StandardTags.RootTag.class) : "root is not a statement"; printNode(node, false); browserInteract(node, frame); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java index 72c99139236f988e32c49eb8cf47458bedabe009..142a26bbddc555a04a1dd018aba329754fc951f5 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/TraceHandling.java @@ -33,9 +33,9 @@ import com.oracle.truffle.api.instrumentation.EventBinding; import com.oracle.truffle.api.instrumentation.EventContext; import com.oracle.truffle.api.instrumentation.ExecutionEventListener; import com.oracle.truffle.api.instrumentation.SourceSectionFilter; -import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.utilities.CyclicAssumption; import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation; +import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag; import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; @@ -88,7 +88,7 @@ public class TraceHandling { if (FastROptions.TraceCalls.getBooleanValue()) { PrimitiveFunctionEntryEventListener fser = new PrimitiveFunctionEntryEventListener(); SourceSectionFilter.Builder builder = SourceSectionFilter.newBuilder(); - builder.tagIs(StandardTags.RootTag.class); + builder.tagIs(FunctionBodyBlockTag.class); SourceSectionFilter filter = builder.build(); RInstrumentation.getInstrumenter().attachExecutionEventListener(filter, fser); setOutputHandler(); @@ -122,7 +122,7 @@ public class TraceHandling { private abstract static class TraceEventListener implements ExecutionEventListener { @CompilationFinal private boolean disabled; - CyclicAssumption disabledUnchangedAssumption = new CyclicAssumption("trace event disabled state unchanged"); + private final CyclicAssumption disabledUnchangedAssumption = new CyclicAssumption("trace event disabled state unchanged"); protected TraceEventListener() { } diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/SpecialCallTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/SpecialCallTest.java index 243a5e9e83510806ab7a5f15bc398e09c7b1d9f3..89a959f4b3d4ee8c19bc2edf8dbb05a55b26ba89 100644 --- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/SpecialCallTest.java +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/SpecialCallTest.java @@ -52,7 +52,7 @@ public class SpecialCallTest extends TestBase { public int special; CountCallsVisitor(RootCallTarget callTarget) { - accept(((RootWithBody) callTarget.getRootNode()).getBody().asRSyntaxNode()); + accept(((RootWithBody) callTarget.getRootNode()).getBody()); } @Override @@ -108,7 +108,7 @@ public class SpecialCallTest extends TestBase { void print(RootCallTarget callTarget) { System.out.println(); - accept(((RootWithBody) callTarget.getRootNode()).getBody().asRSyntaxNode()); + accept(((RootWithBody) callTarget.getRootNode()).getBody()); } @Override diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java index 533db2210151c99af8b2518e8367a45846453b9a..f67c254ce6760e5525ff1ef194c5455be9f1c515 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/LocalReadVariableNode.java @@ -51,7 +51,6 @@ public final class LocalReadVariableNode extends Node { private final Object identifier; private final boolean forceResult; - @CompilationFinal private boolean firstExecution = true; @CompilationFinal(dimensions = 1) private boolean[] seenValueKinds; @CompilationFinal private ValueProfile valueProfile; @@ -145,13 +144,6 @@ public final class LocalReadVariableNode extends Node { isPromiseProfile = ConditionProfile.createBinaryProfile(); } if (isPromiseProfile.profile(result instanceof RPromise)) { - if (firstExecution) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - firstExecution = false; - if (identifier instanceof String) { - return ReadVariableNode.evalPromiseSlowPathWithName((String) identifier, frame, (RPromise) result); - } - } if (promiseHelper == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); promiseHelper = insert(new PromiseHelperNode()); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java index 184b48c6d55ccb37ef6ff74f73136536f303f428..bce05cd02c2a12190891b7dbbe6154f94abfe6a0 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/BlockNode.java @@ -39,7 +39,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; */ public final class BlockNode extends OperatorNode { - @Children protected final RNode[] sequence; + @Children private final RNode[] sequence; @Child private SetVisibilityNode visibility; public BlockNode(SourceSection src, RSyntaxLookup operator, RNode[] sequence) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionBodyNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionBodyNode.java new file mode 100644 index 0000000000000000000000000000000000000000..949b918b9417a8f9ff784fd338b9c3782c9e315a --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionBodyNode.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2018, 2018, 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.r.nodes.function; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.frame.FrameSlot; +import com.oracle.truffle.api.frame.FrameSlotKind; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.r.runtime.RArguments; +import com.oracle.truffle.r.runtime.RArguments.DispatchArgs; +import com.oracle.truffle.r.runtime.RArguments.S3Args; +import com.oracle.truffle.r.runtime.RArguments.S4Args; +import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.RootBodyNode; +import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; +import com.oracle.truffle.r.runtime.nodes.RNode; + +public final class FunctionBodyNode extends Node implements RootBodyNode { + + @Child private RNode body; + @Child private RNode saveArguments; + @Child private SetupS3ArgsNode setupS3Args; + @Child private SetupS4ArgsNode setupS4Args; + + public FunctionBodyNode(RNode saveArguments, RNode body) { + this.body = body; + this.saveArguments = saveArguments; + } + + @Override + public Object visibleExecute(VirtualFrame frame) { + setupDispatchSlots(frame); + saveArguments.execute(frame); + return body.visibleExecute(frame); + } + + @Override + public RNode getBody() { + return body; + } + + @Override + public SourceSection getSourceSection() { + return body.getSourceSection(); + } + + private void setupDispatchSlots(VirtualFrame frame) { + DispatchArgs dispatchArgs = RArguments.getDispatchArgs(frame); + if (dispatchArgs == null) { + return; + } + if (dispatchArgs instanceof S3Args) { + S3Args s3Args = (S3Args) dispatchArgs; + if (setupS3Args == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + setupS3Args = insert(new SetupS3ArgsNode(frame.getFrameDescriptor())); + } + setupS3Args.execute(frame, s3Args); + } else { + S4Args s4Args = (S4Args) dispatchArgs; + if (setupS4Args == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + setupS4Args = insert(new SetupS4ArgsNode(frame.getFrameDescriptor())); + } + setupS4Args.execute(frame, s4Args); + } + } + + private abstract static class SetupDispatchNode extends Node { + // S3/S4 slots + private final FrameSlot dotGenericSlot; + private final FrameSlot dotMethodSlot; + + final BranchProfile invalidateFrameSlotProfile = BranchProfile.create(); + + SetupDispatchNode(FrameDescriptor frameDescriptor) { + dotGenericSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC, FrameSlotKind.Object); + dotMethodSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_METHOD, FrameSlotKind.Object); + } + + void executeDispatchArgs(VirtualFrame frame, DispatchArgs args) { + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericSlot, args.generic, false, invalidateFrameSlotProfile); + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodSlot, args.method, false, invalidateFrameSlotProfile); + } + } + + private static final class SetupS3ArgsNode extends SetupDispatchNode { + // S3 slots + private final FrameSlot dotClassSlot; + private final FrameSlot dotGenericCallEnvSlot; + private final FrameSlot dotGenericCallDefSlot; + private final FrameSlot dotGroupSlot; + + SetupS3ArgsNode(FrameDescriptor frameDescriptor) { + super(frameDescriptor); + dotClassSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_CLASS, FrameSlotKind.Object); + dotGenericCallEnvSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC_CALL_ENV, FrameSlotKind.Object); + dotGenericCallDefSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC_DEF_ENV, FrameSlotKind.Object); + dotGroupSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GROUP, FrameSlotKind.Object); + } + + void execute(VirtualFrame frame, S3Args args) { + super.executeDispatchArgs(frame, args); + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotClassSlot, args.clazz, false, invalidateFrameSlotProfile); + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallEnvSlot, args.callEnv, false, invalidateFrameSlotProfile); + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallDefSlot, args.defEnv, false, invalidateFrameSlotProfile); + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGroupSlot, args.group, false, invalidateFrameSlotProfile); + } + } + + private static final class SetupS4ArgsNode extends SetupDispatchNode { + // S4 slots + private final FrameSlot dotDefinedSlot; + private final FrameSlot dotTargetSlot; + private final FrameSlot dotMethodsSlot; + + SetupS4ArgsNode(FrameDescriptor frameDescriptor) { + super(frameDescriptor); + dotDefinedSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_DEFINED, FrameSlotKind.Object); + dotTargetSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_TARGET, FrameSlotKind.Object); + dotMethodsSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_METHODS, FrameSlotKind.Object); + } + + void execute(VirtualFrame frame, S4Args args) { + super.executeDispatchArgs(frame, args); + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotDefinedSlot, args.defined, false, invalidateFrameSlotProfile); + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotTargetSlot, args.target, false, invalidateFrameSlotProfile); + FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodsSlot, args.methods, false, invalidateFrameSlotProfile); + } + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java index fbb1754702a62025866fb7a5f843c0f22ddce9ef..34712bf4944693f45161fc1241f8a2387d364341 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java @@ -35,7 +35,6 @@ import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.FrameSlotTypeException; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeUtil; import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter; import com.oracle.truffle.api.profiles.BranchProfile; @@ -55,16 +54,13 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.ExitException; import com.oracle.truffle.r.runtime.JumpToTopLevelException; import com.oracle.truffle.r.runtime.RArguments; -import com.oracle.truffle.r.runtime.RArguments.DispatchArgs; -import com.oracle.truffle.r.runtime.RArguments.S3Args; -import com.oracle.truffle.r.runtime.RArguments.S4Args; import com.oracle.truffle.r.runtime.RDeparse; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RErrorHandling; import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.ReturnException; +import com.oracle.truffle.r.runtime.RootBodyNode; import com.oracle.truffle.r.runtime.Utils.DebugExitException; import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.context.RContext; @@ -89,7 +85,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo private final FormalArguments formalArguments; - @Child private RNode body; + @Child private RootBodyNode body; /** * This exists for debugging purposes. It is set initially when the function is defined to @@ -108,7 +104,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo private final RNodeClosureCache closureCache = new RNodeClosureCache(); - @Child private RNode saveArguments; @Child private FrameSlotNode onExitSlot; @Child private InlineCacheNode onExitExpressionCache; private final ConditionProfile onExitProfile = ConditionProfile.createBinaryProfile(); @@ -121,9 +116,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo @Child private PostProcessArgumentsNode argPostProcess; - @Child private SetupS3ArgsNode setupS3Args; - @Child private SetupS4ArgsNode setupS4Args; - private final boolean needsSplitting; @CompilationFinal private boolean containsDispatch; @@ -153,8 +145,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo assert FrameSlotChangeMonitor.isValidFrameDescriptor(frameDesc); assert src != null; this.sourceSectionR = src; - this.saveArguments = saveArguments; - this.body = body.asRNode(); + this.body = new FunctionBodyNode(saveArguments, body.asRNode()); this.name = name; this.onExitSlot = FrameSlotNode.createInitialized(frameDesc, RFrameSlot.OnExit, false); this.needsSplitting = needsAnyBuiltinSplitting(); @@ -269,7 +260,7 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo } public RSyntaxNode getBody() { - return body.asRSyntaxNode(); + return body.getBody().asRSyntaxNode(); } public PostProcessArgumentsNode getArgPostProcess() { @@ -281,8 +272,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo boolean runOnExitHandlers = true; try { verifyEnclosingAssumptions(frame); - setupDispatchSlots(frame); - saveArguments.execute(frame); Object result = body.visibleExecute(frame); normalExit.enter(); if (CompilerDirectives.inInterpreter() && result == null) { @@ -398,91 +387,6 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo } } - private abstract static class SetupDispatchNode extends Node { - // S3/S4 slots - private final FrameSlot dotGenericSlot; - private final FrameSlot dotMethodSlot; - - final BranchProfile invalidateFrameSlotProfile = BranchProfile.create(); - - SetupDispatchNode(FrameDescriptor frameDescriptor) { - dotGenericSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC, FrameSlotKind.Object); - dotMethodSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_METHOD, FrameSlotKind.Object); - } - - void execute(VirtualFrame frame, DispatchArgs args) { - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericSlot, args.generic, false, invalidateFrameSlotProfile); - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodSlot, args.method, false, invalidateFrameSlotProfile); - } - } - - private static final class SetupS3ArgsNode extends SetupDispatchNode { - // S3 slots - private final FrameSlot dotClassSlot; - private final FrameSlot dotGenericCallEnvSlot; - private final FrameSlot dotGenericCallDefSlot; - private final FrameSlot dotGroupSlot; - - SetupS3ArgsNode(FrameDescriptor frameDescriptor) { - super(frameDescriptor); - dotClassSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_CLASS, FrameSlotKind.Object); - dotGenericCallEnvSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC_CALL_ENV, FrameSlotKind.Object); - dotGenericCallDefSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GENERIC_DEF_ENV, FrameSlotKind.Object); - dotGroupSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_GROUP, FrameSlotKind.Object); - } - - void execute(VirtualFrame frame, S3Args args) { - super.execute(frame, args); - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotClassSlot, args.clazz, false, invalidateFrameSlotProfile); - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallEnvSlot, args.callEnv, false, invalidateFrameSlotProfile); - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallDefSlot, args.defEnv, false, invalidateFrameSlotProfile); - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGroupSlot, args.group, false, invalidateFrameSlotProfile); - } - } - - private static final class SetupS4ArgsNode extends SetupDispatchNode { - // S4 slots - private final FrameSlot dotDefinedSlot; - private final FrameSlot dotTargetSlot; - private final FrameSlot dotMethodsSlot; - - SetupS4ArgsNode(FrameDescriptor frameDescriptor) { - super(frameDescriptor); - dotDefinedSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_DEFINED, FrameSlotKind.Object); - dotTargetSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_TARGET, FrameSlotKind.Object); - dotMethodsSlot = FrameSlotChangeMonitor.findOrAddFrameSlot(frameDescriptor, RRuntime.R_DOT_METHODS, FrameSlotKind.Object); - } - - void execute(VirtualFrame frame, S4Args args) { - super.execute(frame, args); - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotDefinedSlot, args.defined, false, invalidateFrameSlotProfile); - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotTargetSlot, args.target, false, invalidateFrameSlotProfile); - FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodsSlot, args.methods, false, invalidateFrameSlotProfile); - } - } - - private void setupDispatchSlots(VirtualFrame frame) { - DispatchArgs dispatchArgs = RArguments.getDispatchArgs(frame); - if (dispatchArgs == null) { - return; - } - if (dispatchArgs instanceof S3Args) { - S3Args s3Args = (S3Args) dispatchArgs; - if (setupS3Args == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - setupS3Args = insert(new SetupS3ArgsNode(frame.getFrameDescriptor())); - } - setupS3Args.execute(frame, s3Args); - } else { - S4Args s4Args = (S4Args) dispatchArgs; - if (setupS4Args == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - setupS4Args = insert(new SetupS4ArgsNode(frame.getFrameDescriptor())); - } - setupS4Args.execute(frame, s4Args); - } - } - private static RPairList getCurrentOnExitList(VirtualFrame frame, FrameSlot slot) { try { return (RPairList) FrameSlotChangeMonitor.getObject(slot, frame); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java index 6f987abcbbe6212c1ee3d90b6fcc47c37eebc22d..1474a417c34bf7d66be37726da02752e677a9e80 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java @@ -787,6 +787,11 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS return arg; } + @Override + public SourceSection getSourceSection() { + return arg.getSourceSection(); + } + @Override public Object execute(VirtualFrame frame) { try { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java index 56581f056d86f41181c127609c525a11ae84f6b4..e83d262ee78ddb1a40352cd94ceddaa022784dbf 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RInstrumentation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -30,6 +30,7 @@ import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode; +import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags.FunctionBodyBlockTag; import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RSource; import com.oracle.truffle.r.runtime.context.RContext; @@ -102,7 +103,7 @@ public class RInstrumentation { public static SourceSectionFilter.Builder createFunctionStartFilter(RFunction func) { FunctionDefinitionNode fdn = (FunctionDefinitionNode) func.getRootNode(); SourceSectionFilter.Builder builder = SourceSectionFilter.newBuilder(); - builder.tagIs(StandardTags.RootTag.class); + builder.tagIs(FunctionBodyBlockTag.class); builder.sourceSectionEquals(fdn.getBody().getSourceSection()); return builder; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RSyntaxTags.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RSyntaxTags.java index 75e32fd0802a978710322964a28bb756e54b5277..becb1090f31cd454e5a51e3b59fc0804751bafe3 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RSyntaxTags.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrumentation/RSyntaxTags.java @@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.instrumentation; import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.instrumentation.Tag; +import com.oracle.truffle.r.runtime.RootBodyNode; public class RSyntaxTags { @@ -33,6 +34,22 @@ public class RSyntaxTags { } } + /** + * Marks a block of statements that is the body of a function, the difference to + * {@link com.oracle.truffle.api.instrumentation.StandardTags.RootTag} is that the + * {@code RootTag} is supposed to save arguments and then invoke the actual body tagged with + * this tag. + * + * More technically, this tag tags {@link com.oracle.truffle.r.nodes.control.BlockNode}s that + * have parent of type {@link RootBodyNode}. + */ + @Tag.Identifier("FUNCTIONBODYBLOCK") + public static final class FunctionBodyBlockTag extends Tag { + private FunctionBodyBlockTag() { + // no instances + } + } + @SuppressWarnings("unchecked") public static final Class<? extends Tag>[] ALL_TAGS = (Class<? extends Tag>[]) new Class<?>[]{StandardTags.CallTag.class, StandardTags.StatementTag.class, - StandardTags.RootTag.class, LoopTag.class}; + StandardTags.RootTag.class, LoopTag.class, FunctionBodyBlockTag.class}; } diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java index f92d3697ba5833dfbe6f37da88c4e8f578bb0a56..d05bcab85c9f53cf7b617f2441c72612acdf43b5 100644 --- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java +++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/DefaultRParserFactory.java @@ -62,6 +62,26 @@ public class DefaultRParserFactory extends RParserFactory { } } + @Override + public List<T> statements(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException { + try { + try { + RContext context = language.getContextReference().get(); + RParser<T> parser = new RParser<>(source, fullSource, startLine, builder, language, context.sourceCache); + return parser.script(); + } catch (IllegalArgumentException e) { + // the lexer will wrap exceptions in IllegalArgumentExceptions + if (e.getCause() instanceof RecognitionException) { + throw (RecognitionException) e.getCause(); + } else { + throw e; + } + } + } catch (RecognitionException e) { + throw handleRecognitionException(source, e); + } + } + @Override public RootCallTarget rootFunction(Source source, String name, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException { RContext context = language.getContextReference().get(); diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java index c54448034f70222006046e7952cb8762e17034b6..be131a7a7da9e1fd430fb2d8b5abf352fb2f2f89 100644 --- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java +++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/ParserGeneration.java @@ -98,6 +98,7 @@ public class ParserGeneration { "allow everything but newlines in %<ident>% operators", "allow strings in :: and :::", "use file for interactive single-line source", - ":: and ::: do not set argument names" + ":: and ::: do not set argument names", + "refactored: use file for interactive single-line source" }; } diff --git a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g index daab0541ae8da9e5eb327ad196c7950d7165a873..3dbf82f7b36a8bf3370f549c291ca6d8004327b8 100644 --- a/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g +++ b/com.oracle.truffle.r.parser/src/com/oracle/truffle/r/parser/R.g @@ -75,90 +75,25 @@ import com.oracle.truffle.r.runtime.RError; private TruffleRLanguage language; private int fileStartOffset = 0; private Map<String, Source> sourceCache; - - /** - * Always use this constructor to initialize the R specific fields. - */ + public RParser(Source source, RCodeBuilder<T> builder, TruffleRLanguage language, Map<String, Source> sourceCache) { super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCharacters().toString())))); assert source != null && builder != null; this.initialSource = source; this.builder = builder; this.language = language; - this.sourceCache = sourceCache; - if (source.getURI() != null && source.getName().contains("#")) { - this.source = createFullSource(source); - } else { - this.source = source; - } + this.source = source; } - - private Source createFullSource(Source original) { - String originalName = original.getName(); - // check if source name is like 'path/to/source.R#45-54' - int hash_idx = originalName.lastIndexOf("#"); - if (hash_idx == -1) { - return original; - } - - String fileName = originalName.substring(0, hash_idx); - String lineRange = originalName.substring(hash_idx + 1); - - try { - // check for line range, e.g. '45-54' - int startLine = -1; - int endLine = -1; - int dashIdx = lineRange.indexOf('-'); - if (dashIdx != -1) { - startLine = Integer.parseInt(lineRange.substring(0, dashIdx)); - endLine = Integer.parseInt(lineRange.substring(dashIdx + 1)); - } else { - startLine = Integer.parseInt(lineRange); - endLine = startLine; - } - File f = new File(fileName); - Source fullSource; - String canonicalName; - try { - canonicalName = f.getAbsoluteFile().getCanonicalPath(); - fullSource = sourceCache != null ? sourceCache.get(canonicalName) : null; - } catch(IOException e) { - // ignore an freshly load file - fullSource = null; - canonicalName = null; - } - if(fullSource == null) { - Builder<IOException, RuntimeException, RuntimeException> newBuilder = Source.newBuilder(f); - if (original.isInteractive()) { - newBuilder.interactive(); - } - fullSource = newBuilder.build(); - - if (sourceCache != null && canonicalName != null) { - sourceCache.put(canonicalName, fullSource); - } - } - - // verify to avoid accidentally matching file names - for (int i = 0; i < endLine - startLine + 1; i++) { - if (!original.getCharacters(i + 1).equals(fullSource.getCharacters(startLine + i))) { - return original; - } - } - - fileStartOffset = -fullSource.getLineStartOffset(startLine); - return fullSource; - } catch (NumberFormatException e) { - // invalid line number - } catch (IllegalArgumentException e) { - // file name is accidentally named in the expected scheme - } catch (IOException e) { - } catch (RuntimeException e) { - assert rethrow(e); - } - return original; - } + public RParser(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language, Map<String, Source> sourceCache) { + super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCharacters().toString())))); + assert source != null && builder != null; + this.initialSource = source; + this.builder = builder; + this.language = language; + this.source = fullSource; + fileStartOffset = -fullSource.getLineStartOffset(startLine); + } private <T extends Throwable> boolean rethrow(T e) throws T { throw e; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java index f42227d82e276a80c92f214e516232225a6cdf2b..e49e36e7f82ef7ad5af92d6ec050330bad2bbd12 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RParserFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, 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 @@ -37,6 +37,8 @@ public abstract class RParserFactory { public interface Parser<T> { List<T> script(Source source, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException; + List<T> statements(Source source, Source fullSource, int startLine, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException; + RootCallTarget rootFunction(Source source, String name, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException; boolean isRecognitionException(Throwable t); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootBodyNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootBodyNode.java new file mode 100644 index 0000000000000000000000000000000000000000..68031a62f3c1f75120ec40ea4ebddbde999edaaa --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootBodyNode.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, 2018, 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.r.runtime; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrumentation.ProbeNode; +import com.oracle.truffle.api.instrumentation.StandardTags.RootTag; +import com.oracle.truffle.api.instrumentation.Tag; +import com.oracle.truffle.r.runtime.nodes.RInstrumentableNode; +import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.instrumentation.RRootBodyNodeWrapper; + +/** + * Marks a node that represents the body of a {@link com.oracle.truffle.api.nodes.RootNode}, such + * nodes are tagged with {@link com.oracle.truffle.api.instrumentation.StandardTags.RootTag} and + * should inherit {@link RNode}. + * + * The {@link RootBodyNode} task is to save the arguments from frame's arguments array to the local + * variables and then invoke the actual body statement accessible via {@link #getBody()}. + */ +public interface RootBodyNode extends RInstrumentableNode { + RNode getBody(); + + Object visibleExecute(VirtualFrame frame); + + @Override + default boolean isInstrumentable() { + return true; + } + + @Override + default boolean hasTag(Class<? extends Tag> tag) { + return tag == RootTag.class; + } + + @Override + default WrapperNode createWrapper(ProbeNode probe) { + return new RRootBodyNodeWrapper(this, probe); + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootWithBody.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootWithBody.java index b2736e10e5e517f3d2d9d73c1109e44cfdfdd3ba..2af8f9a54a5af6bd615349b9adc13503be91ad58 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootWithBody.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RootWithBody.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2018, 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 @@ -22,12 +22,16 @@ */ package com.oracle.truffle.r.runtime; -import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; /** - * Used for testing. + * Used to allow access to the body field for testing purposes. */ public interface RootWithBody { - RNode getBody(); + /** + * Should return the real body, i.e. what is wrapped by {@link RootBodyNode}. + */ + RSyntaxNode getBody(); + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/instrumentation/RRootBodyNodeWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/instrumentation/RRootBodyNodeWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..37ca385062c29fb441a49316f962b6ac6b015586 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/instrumentation/RRootBodyNodeWrapper.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018, 2018, 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.r.runtime.nodes.instrumentation; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrumentation.InstrumentableNode; +import com.oracle.truffle.api.instrumentation.ProbeNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeCost; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.r.runtime.RootBodyNode; +import com.oracle.truffle.r.runtime.nodes.RNode; + +@NodeInfo(cost = NodeCost.NONE) +public final class RRootBodyNodeWrapper extends Node implements RootBodyNode, InstrumentableNode.WrapperNode { + @Child private RootBodyNode delegate; + @Child private ProbeNode probeNode; + + public RRootBodyNodeWrapper(RootBodyNode delegate, ProbeNode probeNode) { + assert delegate != null; + assert !(delegate instanceof RRootBodyNodeWrapper); + this.delegate = delegate; + this.probeNode = probeNode; + } + + @Override + public Node getDelegateNode() { + return (Node) delegate; + } + + @Override + public ProbeNode getProbeNode() { + return probeNode; + } + + @Override + public Object visibleExecute(VirtualFrame frame) { + Object returnValue; + for (;;) { + boolean wasOnReturnExecuted = false; + try { + probeNode.onEnter(frame); + returnValue = delegate.visibleExecute(frame); + wasOnReturnExecuted = true; + probeNode.onReturnValue(frame, returnValue); + break; + } catch (Throwable t) { + Object result = probeNode.onReturnExceptionalOrUnwind(frame, t, wasOnReturnExecuted); + if (result == ProbeNode.UNWIND_ACTION_REENTER) { + continue; + } else if (result != null) { + returnValue = result; + break; + } + throw t; + } + } + return returnValue; + } + + @Override + public RNode getBody() { + return delegate.getBody(); + } + + @Override + public SourceSection getSourceSection() { + return ((Node) delegate).getSourceSection(); + } +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java index 00e515d31f38a6905516bed94d2d79e31c60714b..a18d440f52151239237a8260a2c58ad206482d6f 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java @@ -39,7 +39,6 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import com.oracle.truffle.api.debug.Breakpoint; @@ -498,7 +497,6 @@ public class FastRDebugTest { } @Test - @Ignore public void testReenterArgumentsAndValues() throws Throwable { // Test that after a re-enter, arguments are kept and variables are cleared. final Source source = sourceFromText("" + @@ -719,7 +717,7 @@ public class FastRDebugTest { } assertNotNull("identifier \"" + expectedIdentifier + "\" not found", value); String valueStr = value.as(String.class); - assertEquals(expectedValueStr, valueStr); + assertEquals(line + ": " + code + "; identifier: '" + expectedIdentifier + "'", expectedValueStr, valueStr); } if (!run.isEmpty()) { diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py index 7e17fff9837880a077c7eb7b3c5904bb0a86be44..3d7ca02b4b00d7c08a2ce76cafbfbc1b28b5dd72 100644 --- a/mx.fastr/suite.py +++ b/mx.fastr/suite.py @@ -231,6 +231,7 @@ suite = { "sourceDirs" : ["src"], "dependencies" : [ "sdk:GRAAL_SDK", + "sdk:LAUNCHER_COMMON", "truffle:JLINE", ], "checkstyle" : "com.oracle.truffle.r.runtime",