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
No related branches found
No related tags found
No related merge requests found
......@@ -183,9 +183,8 @@ public class RCommand {
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)
* it goes through the console. N.B. -f and -e can't be used together and this is already
* checked.
* Whether the input is from stdin, a file (-f), or an expression on the command line (-e) it goes
* through the console. N.B. -f and -e can't be used together and this is already checked.
*/
RStartParams rsp = new RStartParams(options, false);
String fileArgument = rsp.getFileArgument();
......@@ -193,8 +192,8 @@ public class RCommand {
List<String> lines;
try {
/*
* If initial==false, ~ expansion will not have been done and the open will fail.
* It's harmless to always do it.
* If initial==false, ~ expansion will not have been done and the open will fail. It's harmless to
* always do it.
*/
File file = fileArgument.startsWith("~") ? new File(System.getProperty("user.home") + fileArgument.substring(1)) : new File(fileArgument);
lines = Files.readAllLines(file.toPath());
......@@ -216,9 +215,8 @@ public class RCommand {
boolean useReadLine = isInteractive && !rsp.noReadline();
if (useDelegatingWrapper != null) {
/*
* If we are in embedded mode, the creation of ConsoleReader and the ConsoleHandler
* should be lazy, as these may not be necessary and can cause hangs if stdin has
* been redirected.
* If we are in embedded mode, the creation of ConsoleReader and the ConsoleHandler should be lazy,
* as these may not be necessary and can cause hangs if stdin has been redirected.
*/
Supplier<ConsoleHandler> delegateFactory = useReadLine ? () -> new JLineConsoleHandler(inStream, outStream, rsp.isSlave())
: () -> new DefaultConsoleHandler(inStream, outStream, isInteractive);
......@@ -251,14 +249,14 @@ public class RCommand {
}
/**
* The read-eval-print loop, which can take input from a console, command line expression or a
* file. There are two ways the repl can terminate:
* The read-eval-print loop, which can take input from a console, command line expression or a file.
* There are two ways the repl can terminate:
* <ol>
* <li>A {@code quit} command is executed successfully.</li>
* <li>EOF on the input.</li>
* </ol>
* In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before
* exiting. So,in either case, we never return.
* In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before exiting.
* So,in either case, we never return.
*/
public static int readEvalPrint(Context context, ConsoleHandler consoleHandler, File srcFile) {
int lastStatus = 0;
......@@ -286,7 +284,7 @@ public class RCommand {
Source src;
if (srcFile != null) {
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 {
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.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -34,6 +34,7 @@ import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.r.runtime.RParserFactory;
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.RContext;
import com.oracle.truffle.r.runtime.context.TruffleRLanguage;
import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
......@@ -45,7 +46,8 @@ public class DefaultRParserFactory extends RParserFactory {
public List<T> script(Source source, RCodeBuilder<T> builder, TruffleRLanguage language) throws ParseException {
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();
} catch (IllegalArgumentException e) {
// the lexer will wrap exceptions in IllegalArgumentExceptions
......@@ -62,7 +64,8 @@ public class DefaultRParserFactory extends RParserFactory {
@Override
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 {
return parser.root_function(name);
} catch (RecognitionException e) {
......
......@@ -26,14 +26,15 @@ options {
//@formatter:off
package com.oracle.truffle.r.parser;
import java.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
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.frame.MaterializedFrame;
......@@ -73,16 +74,18 @@ import com.oracle.truffle.r.runtime.RError;
private RCodeBuilder<T> builder;
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) {
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 {
......@@ -91,53 +94,75 @@ import com.oracle.truffle.r.runtime.RError;
}
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 originalName = original.getName();
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;
}
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;
}
}
// check if source name is like 'path/to/source.R#45-54'
int hash_idx = originalName.lastIndexOf("#");
if (hash_idx == -1) {
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) {
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);
}
}
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.
......
......@@ -361,6 +361,7 @@ public final class RContext {
public final List<String> libraryPaths = new ArrayList<>(1);
public final Map<Integer, Thread> threads = new ConcurrentHashMap<>();
public final LanguageClosureCache languageClosureCache = new LanguageClosureCache();
public final Map<String, Source> sourceCache = new ConcurrentHashMap<>();
private final AllocationReporter allocationReporter;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment