Skip to content
Snippets Groups Projects
Commit ef61682c authored by Florian Angerer's avatar Florian Angerer
Browse files

Introduce source cache in RParser.

parent 7d90de1f
Branches
No related tags found
No related merge requests found
...@@ -183,9 +183,8 @@ public class RCommand { ...@@ -183,9 +183,8 @@ public class RCommand {
public static ConsoleHandler createConsoleHandler(RCmdOptions options, DelegatingConsoleHandler useDelegatingWrapper, InputStream inStream, OutputStream outStream) { public static ConsoleHandler createConsoleHandler(RCmdOptions options, DelegatingConsoleHandler useDelegatingWrapper, InputStream inStream, OutputStream outStream) {
/* /*
* Whether the input is from stdin, a file (-f), or an expression on the command line (-e) * Whether the input is from stdin, a file (-f), or an expression on the command line (-e) it goes
* it goes through the console. N.B. -f and -e can't be used together and this is already * through the console. N.B. -f and -e can't be used together and this is already checked.
* checked.
*/ */
RStartParams rsp = new RStartParams(options, false); RStartParams rsp = new RStartParams(options, false);
String fileArgument = rsp.getFileArgument(); String fileArgument = rsp.getFileArgument();
...@@ -193,8 +192,8 @@ public class RCommand { ...@@ -193,8 +192,8 @@ public class RCommand {
List<String> lines; List<String> lines;
try { try {
/* /*
* If initial==false, ~ expansion will not have been done and the open will fail. * If initial==false, ~ expansion will not have been done and the open will fail. It's harmless to
* It's harmless to always do it. * always do it.
*/ */
File file = fileArgument.startsWith("~") ? new File(System.getProperty("user.home") + fileArgument.substring(1)) : new File(fileArgument); File file = fileArgument.startsWith("~") ? new File(System.getProperty("user.home") + fileArgument.substring(1)) : new File(fileArgument);
lines = Files.readAllLines(file.toPath()); lines = Files.readAllLines(file.toPath());
...@@ -216,9 +215,8 @@ public class RCommand { ...@@ -216,9 +215,8 @@ public class RCommand {
boolean useReadLine = isInteractive && !rsp.noReadline(); boolean useReadLine = isInteractive && !rsp.noReadline();
if (useDelegatingWrapper != null) { if (useDelegatingWrapper != null) {
/* /*
* If we are in embedded mode, the creation of ConsoleReader and the ConsoleHandler * If we are in embedded mode, the creation of ConsoleReader and the ConsoleHandler should be lazy,
* should be lazy, as these may not be necessary and can cause hangs if stdin has * as these may not be necessary and can cause hangs if stdin has been redirected.
* been redirected.
*/ */
Supplier<ConsoleHandler> delegateFactory = useReadLine ? () -> new JLineConsoleHandler(inStream, outStream, rsp.isSlave()) Supplier<ConsoleHandler> delegateFactory = useReadLine ? () -> new JLineConsoleHandler(inStream, outStream, rsp.isSlave())
: () -> new DefaultConsoleHandler(inStream, outStream, isInteractive); : () -> new DefaultConsoleHandler(inStream, outStream, isInteractive);
...@@ -251,14 +249,14 @@ public class RCommand { ...@@ -251,14 +249,14 @@ public class RCommand {
} }
/** /**
* The read-eval-print loop, which can take input from a console, command line expression or a * The read-eval-print loop, which can take input from a console, command line expression or a file.
* file. There are two ways the repl can terminate: * There are two ways the repl can terminate:
* <ol> * <ol>
* <li>A {@code quit} command is executed successfully.</li> * <li>A {@code quit} command is executed successfully.</li>
* <li>EOF on the input.</li> * <li>EOF on the input.</li>
* </ol> * </ol>
* In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before * In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before exiting.
* exiting. So,in either case, we never return. * So,in either case, we never return.
*/ */
public static int readEvalPrint(Context context, ConsoleHandler consoleHandler, File srcFile) { public static int readEvalPrint(Context context, ConsoleHandler consoleHandler, File srcFile) {
int lastStatus = 0; int lastStatus = 0;
...@@ -286,7 +284,7 @@ public class RCommand { ...@@ -286,7 +284,7 @@ public class RCommand {
Source src; Source src;
if (srcFile != null) { if (srcFile != null) {
int endLine = consoleHandler.getCurrentLineIndex(); int endLine = consoleHandler.getCurrentLineIndex();
src = Source.newBuilder("R", sb.toString(), srcFile.getName() + "#" + startLine + "-" + endLine).interactive(true).uri(srcFile.toURI()).buildLiteral(); src = Source.newBuilder("R", sb.toString(), srcFile.toString() + "#" + startLine + "-" + endLine).interactive(true).uri(srcFile.toURI()).buildLiteral();
} else { } else {
src = Source.newBuilder("R", sb.toString(), "<REPL>").interactive(true).buildLiteral(); src = Source.newBuilder("R", sb.toString(), "<REPL>").interactive(true).buildLiteral();
} }
......
/* /*
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -34,6 +34,7 @@ import com.oracle.truffle.api.source.Source; ...@@ -34,6 +34,7 @@ import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.r.runtime.RParserFactory; import com.oracle.truffle.r.runtime.RParserFactory;
import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException; import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException;
import com.oracle.truffle.r.runtime.context.Engine.ParseException; import com.oracle.truffle.r.runtime.context.Engine.ParseException;
import com.oracle.truffle.r.runtime.context.RContext;
import com.oracle.truffle.r.runtime.context.TruffleRLanguage; import com.oracle.truffle.r.runtime.context.TruffleRLanguage;
import com.oracle.truffle.r.runtime.nodes.RCodeBuilder; import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
...@@ -45,7 +46,8 @@ public class DefaultRParserFactory extends RParserFactory { ...@@ -45,7 +46,8 @@ public class DefaultRParserFactory extends RParserFactory {
public List<T> script(Source source, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException { public List<T> script(Source source, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException {
try { try {
try { try {
RParser<T> parser = new RParser<>(source, builder, language); RContext context = language.getContextReference().get();
RParser<T> parser = new RParser<>(source, builder, language, context.sourceCache);
return parser.script(); return parser.script();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// the lexer will wrap exceptions in IllegalArgumentExceptions // the lexer will wrap exceptions in IllegalArgumentExceptions
...@@ -62,7 +64,8 @@ public class DefaultRParserFactory extends RParserFactory { ...@@ -62,7 +64,8 @@ public class DefaultRParserFactory extends RParserFactory {
@Override @Override
public RootCallTarget rootFunction(Source source, String name, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException { public RootCallTarget rootFunction(Source source, String name, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException {
RParser<T> parser = new RParser<>(source, builder, language); RContext context = language.getContextReference().get();
RParser<T> parser = new RParser<>(source, builder, language, context.sourceCache);
try { try {
return parser.root_function(name); return parser.root_function(name);
} catch (RecognitionException e) { } catch (RecognitionException e) {
......
...@@ -26,14 +26,15 @@ options { ...@@ -26,14 +26,15 @@ options {
//@formatter:off //@formatter:off
package com.oracle.truffle.r.parser; package com.oracle.truffle.r.parser;
import java.util.ArrayList;
import java.util.List;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.net.URISyntaxException; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.MaterializedFrame;
...@@ -73,16 +74,18 @@ import com.oracle.truffle.r.runtime.RError; ...@@ -73,16 +74,18 @@ import com.oracle.truffle.r.runtime.RError;
private RCodeBuilder<T> builder; private RCodeBuilder<T> builder;
private TruffleRLanguage language; private TruffleRLanguage language;
private int fileStartOffset = 0; private int fileStartOffset = 0;
private Map<String, Source> sourceCache;
/** /**
* Always use this constructor to initialize the R specific fields. * Always use this constructor to initialize the R specific fields.
*/ */
public RParser(Source source, RCodeBuilder<T> builder, TruffleRLanguage language) { public RParser(Source source, RCodeBuilder<T> builder, TruffleRLanguage language, Map<String, Source> sourceCache) {
super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCharacters().toString())))); super(new CommonTokenStream(new RLexer(new ANTLRStringStream(source.getCharacters().toString()))));
assert source != null && builder != null; assert source != null && builder != null;
this.initialSource = source; this.initialSource = source;
this.builder = builder; this.builder = builder;
this.language = language; this.language = language;
this.sourceCache = sourceCache;
if (source.getURI() != null && source.getName().contains("#")) { if (source.getURI() != null && source.getName().contains("#")) {
this.source = createFullSource(source); this.source = createFullSource(source);
} else { } else {
...@@ -91,53 +94,75 @@ import com.oracle.truffle.r.runtime.RError; ...@@ -91,53 +94,75 @@ import com.oracle.truffle.r.runtime.RError;
} }
private Source createFullSource(Source original) { private Source createFullSource(Source original) {
String originalName = original.getName(); 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); // check if source name is like 'path/to/source.R#45-54'
String lineRange = originalName.substring(hash_idx + 1); int hash_idx = originalName.lastIndexOf("#");
if (hash_idx == -1) {
try { return original;
// 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;
}
Builder<IOException, RuntimeException, RuntimeException> newBuilder = Source.newBuilder(new File(fileName));
if (original.isInteractive()) {
newBuilder.interactive();
}
Source fullSource = newBuilder.build();
// 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); String fileName = originalName.substring(0, hash_idx);
return fullSource; String lineRange = originalName.substring(hash_idx + 1);
} catch (NumberFormatException e) {
// invalid line number try {
} catch (IllegalArgumentException e) { // check for line range, e.g. '45-54'
// file name is accidentally named in the expected scheme int startLine = -1;
} catch (IOException e) { int endLine = -1;
} catch (RuntimeException e) { 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);
}
} }
return original;
} // 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;
}
private <T extends Throwable> boolean rethrow(T e) throws T {
throw e;
}
/** /**
* Helper function that returns the last parsed token, usually used for building source sections. * Helper function that returns the last parsed token, usually used for building source sections.
......
...@@ -361,6 +361,7 @@ public final class RContext { ...@@ -361,6 +361,7 @@ public final class RContext {
public final List<String> libraryPaths = new ArrayList<>(1); public final List<String> libraryPaths = new ArrayList<>(1);
public final Map<Integer, Thread> threads = new ConcurrentHashMap<>(); public final Map<Integer, Thread> threads = new ConcurrentHashMap<>();
public final LanguageClosureCache languageClosureCache = new LanguageClosureCache(); public final LanguageClosureCache languageClosureCache = new LanguageClosureCache();
public final Map<String, Source> sourceCache = new ConcurrentHashMap<>();
private final AllocationReporter allocationReporter; private final AllocationReporter allocationReporter;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment