diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RContextFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RContextFactory.java index e32f9ec9a61cf342a453d5c515c61c96103266ef..a3c3e05c2d9c8866a73a94ba092256e509a8ba52 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RContextFactory.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RContextFactory.java @@ -22,12 +22,17 @@ */ package com.oracle.truffle.r.engine; +import java.io.*; import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import com.oracle.truffle.api.vm.*; +import com.oracle.truffle.api.vm.TruffleVM.Builder; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.instrument.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.ffi.*; /** @@ -42,52 +47,47 @@ public class RContextFactory { */ static { Load_RFFIFactory.initialize(); + RInstrument.initialize(); + RPerfStats.initialize(); + Locale.setDefault(Locale.ROOT); + RAccuracyInfo.initialize(); + RVersionInfo.initialize(); + TempPathName.initialize(); + RContext.initialize(new RRuntimeASTAccessImpl(), RBuiltinPackages.getInstance(), FastROptions.IgnoreVisibility); } - private static boolean initialized; + private static final Semaphore createSemaphore = new Semaphore(1, true); /** - * Initialize all the context-independent aspects of the system. + * Create a context of given kind. */ - public static void initialize() { - if (!initialized) { - FastROptions.initialize(); - REnvVars.initialize(); - RInstrument.initialize(); - RPerfStats.initialize(); - Locale.setDefault(Locale.ROOT); - RAccuracyInfo.initialize(); - RVersionInfo.initialize(); - TempPathName.initialize(); - RProfile.initialize(); - RContext.initialize(new RRuntimeASTAccessImpl(), RBuiltinPackages.getInstance(), FastROptions.IgnoreVisibility.getValue()); - initialized = true; + public static TruffleVM create(ContextInfo info, Consumer<TruffleVM.Builder> setup) { + try { + createSemaphore.acquire(); + RContext.tempInitializingContextInfo = info; + Builder builder = TruffleVM.newVM(); + if (setup != null) { + setup.accept(builder); + } + TruffleVM vm = builder.build(); + try { + vm.eval(TruffleRLanguage.MIME, "invisible(1)"); + } catch (IOException e) { + createSemaphore.release(); + throw RInternalError.shouldNotReachHere(e); + } + RContext.associate(vm); + createSemaphore.release(); + return vm; + } catch (InterruptedException x) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "Error creating parallel R runtime instance"); } } - public static RContext createShareParentReadWrite(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) { - RContext context = RContext.createShareParentReadWrite(parent, commandArgs, consoleHandler); - return context; - } - - public static RContext createShareParentReadOnly(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) { - RContext context = RContext.createShareParentReadOnly(parent, commandArgs, consoleHandler); - return context; - } - - /** - * Create the initial context with no parent. - */ - public static RContext createInitial(String[] commandArgs, ConsoleHandler consoleHandler) { - RContext context = RContext.createShareNothing(null, commandArgs, consoleHandler); - return context; - } - /** * Create a context of given kind. */ - public static RContext create(RContext parent, Kind kind, String[] commandArgs, ConsoleHandler consoleHandler) { - RContext context = RContext.create(parent, kind, commandArgs, consoleHandler); - return context; + public static TruffleVM create(ContextInfo info) { + return create(info, null); } } 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 b6473efd77d8fe5b9f11d02fe87f42b49b74da25..737f8b3c1547a86c4344402613d31458db012c13 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,23 +22,24 @@ */ package com.oracle.truffle.r.engine; -import static com.oracle.truffle.r.runtime.RCmdOptions.NO_RESTORE; - import java.io.*; import java.util.*; import java.util.stream.*; import org.antlr.runtime.*; +import com.oracle.truffle.api.*; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.impl.*; import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.interop.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.vm.*; +import com.oracle.truffle.r.engine.interop.*; import com.oracle.truffle.r.library.graphics.*; import com.oracle.truffle.r.nodes.*; import com.oracle.truffle.r.nodes.access.*; @@ -50,14 +51,13 @@ import com.oracle.truffle.r.nodes.runtime.*; import com.oracle.truffle.r.parser.*; import com.oracle.truffle.r.parser.ast.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.ConsoleHandler; +import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption; import com.oracle.truffle.r.runtime.Utils.DebugExitException; -import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; -import com.oracle.truffle.r.runtime.env.REnvironment.PutException; import com.oracle.truffle.r.runtime.env.frame.*; import com.oracle.truffle.r.runtime.nodes.*; @@ -65,7 +65,7 @@ import com.oracle.truffle.r.runtime.nodes.*; * The engine for the FastR implementation. Handles parsing and evaluation. There is one instance of * this class per {@link RContext}. */ -final class REngine implements RContext.Engine { +final class REngine implements Engine { /** * Controls the behavior when an implementation errors occurs. In normal use this is fatal as @@ -87,14 +87,6 @@ final class REngine implements RContext.Engine { */ private final RContext context; - /** - * Every engine has an associated {@link TruffleVM}. The actual {@link TruffleVM} instance is - * not "built" until the {@link #activate} method is invoked. - */ - private final TruffleVM.Builder truffleVMBuilder; - - @CompilationFinal private TruffleVM truffleVM; - /** * The unique frame for the global environment for this engine. */ @@ -116,7 +108,6 @@ final class REngine implements RContext.Engine { } private REngine(RContext context) { - this.truffleVMBuilder = TruffleVM.newVM(); this.context = context; this.childTimes = new long[]{0, 0}; } @@ -126,12 +117,10 @@ final class REngine implements RContext.Engine { return engine; } - public void activate() { - truffleVM = truffleVMBuilder.build(); - this.globalFrame = RRuntime.createNonFunctionFrame().materialize(); + public void activate(REnvironment.ContextStateImpl stateREnvironment) { + this.globalFrame = stateREnvironment.getGlobalFrame(); this.startTime = System.nanoTime(); - context.installCustomClassState(RContext.ClassStateKind.REnvironment, new REnvironment.ClassStateFactory().newContext(context, globalFrame)); - if (context.getKind() == RContext.Kind.SHARE_NOTHING) { + if (context.getKind() == RContext.ContextKind.SHARE_NOTHING) { initializeShared(); } } @@ -140,7 +129,7 @@ final class REngine implements RContext.Engine { suppressWarnings = true; MaterializedFrame baseFrame = RRuntime.createNonFunctionFrame().materialize(); REnvironment.baseInitialize(baseFrame, globalFrame); - loadBase = FastROptions.LoadBase.getValue(); + loadBase = FastROptions.LoadBase; RBuiltinPackages.loadBase(baseFrame, loadBase); RGraphics.initialize(); if (loadBase) { @@ -148,19 +137,31 @@ final class REngine implements RContext.Engine { * eval the system/site/user profiles. Experimentally GnuR does not report warnings * during system profile evaluation, but does for the site/user profiles. */ - parseAndEval(RProfile.systemProfile(), baseFrame, false, false); + try { + parseAndEval(RProfile.systemProfile(), baseFrame, false); + } catch (ParseException e) { + throw new RInternalError(e, "error while parsing system profile from %s", RProfile.systemProfile().getName()); + } checkAndRunStartupFunction(".OptRequireMethods"); suppressWarnings = false; - Source siteProfile = RProfile.siteProfile(); + Source siteProfile = context.stateRProfile.siteProfile(); if (siteProfile != null) { - parseAndEval(siteProfile, baseFrame, false, false); + try { + parseAndEval(siteProfile, baseFrame, false); + } catch (ParseException e) { + throw new RInternalError(e, "error while parsing site profile from %s", siteProfile.getName()); + } } - Source userProfile = RProfile.userProfile(); + Source userProfile = context.stateRProfile.userProfile(); if (userProfile != null) { - parseAndEval(userProfile, globalFrame, false, false); + try { + parseAndEval(userProfile, globalFrame, false); + } catch (ParseException e) { + throw new RInternalError(e, "error while parsing user profile from %s", userProfile.getName()); + } } - if (!NO_RESTORE.getValue()) { + if (!context.getOptions().getBoolean(RCmdOption.NO_RESTORE)) { /* * TODO This is where we would load any saved user data */ @@ -169,7 +170,6 @@ final class REngine implements RContext.Engine { checkAndRunStartupFunction(".First.sys"); RBuiltinPackages.loadDefaultPackageOverrides(); } - context.systemInitialized(); } private void checkAndRunStartupFunction(String name) { @@ -182,7 +182,11 @@ final class REngine implements RContext.Engine { RInstrument.checkDebugRequested(name, (RFunction) func); String call = name + "()"; // Should this print the result? - parseAndEval(Source.fromText(call, "<startup>"), globalFrame, false, false); + try { + parseAndEval(Source.fromText(call, "<startup>"), globalFrame, false); + } catch (ParseException e) { + throw new RInternalError(e, "error while parsing startup function"); + } } } @@ -198,19 +202,12 @@ final class REngine implements RContext.Engine { return childTimes; } - public TruffleVM getTruffleVM() { - assert truffleVM != null; - return truffleVM; - } - - public TruffleVM.Builder getTruffleVMBuilder() { - return truffleVMBuilder; - } - @Override - public Object parseAndEval(Source source, MaterializedFrame frame, boolean printResult, boolean allowIncompleteSource) { + public Object parseAndEval(Source source, MaterializedFrame frame, boolean printResult) throws ParseException { + RSyntaxNode node = transform(parseImpl(source)); + RootCallTarget callTarget = doMakeCallTarget(node.asRNode(), "<repl wrapper>"); try { - return parseAndEvalImpl(source, frame, printResult, allowIncompleteSource); + return runCall(callTarget, frame, printResult, true); } catch (ReturnException ex) { return ex.getResult(); } catch (DebugExitException | QuitException | BrowserQuitException e) { @@ -233,99 +230,79 @@ final class REngine implements RContext.Engine { } @Override - public Object parseAndEval(Source source, boolean printResult, boolean allowIncompleteSource) { - return parseAndEval(source, globalFrame, printResult, allowIncompleteSource); + public Object parseAndEval(Source source, boolean printResult) throws ParseException { + return parseAndEval(source, globalFrame, printResult); } - @Override - public Object parseAndEvalTest(Source source, boolean printResult, boolean allowIncompleteSource) { + private static ASTNode parseImpl(Source source) throws ParseException { try { - return parseAndEvalImpl(source, globalFrame, printResult, allowIncompleteSource); - } catch (RInternalError e) { - context.getConsoleHandler().printErrorln("FastR internal error: " + e.getMessage()); - RInternalError.reportError(e); - throw e; + return ParseUtil.parseAST(new ANTLRStringStream(source.getCode()), source); } catch (RecognitionException e) { - throw new RInternalError(e, "recognition exception"); - } - } - - private Object parseAndEvalImpl(Source source, MaterializedFrame frame, boolean printResult, boolean allowIncompleteSource) throws RecognitionException { - RSyntaxNode node; - try { - node = parseToRNode(source); - } catch (NoViableAltException | MismatchedTokenException e) { - if (e.token.getType() == Token.EOF && allowIncompleteSource) { + String line = e.line <= source.getLineCount() ? source.getCode(e.line) : ""; + String substring = line.substring(0, Math.min(line.length(), e.charPositionInLine + 1)); + String token = e.token.getText(); + if (e.token.getType() == Token.EOF && (e instanceof NoViableAltException || e instanceof MismatchedTokenException)) { // the parser got stuck at the eof, request another line - return INCOMPLETE_SOURCE; + throw new IncompleteSourceException(e, source, token, substring, e.line); + } else { + throw new ParseException(e, source, token, substring, e.line); } - String line = source.getCode(e.line); - String message = "Error: unexpected '" + e.token.getText() + "' in \"" + line.substring(0, Math.min(line.length(), e.charPositionInLine + 1)) + "\""; - writeStderr(source.getLineCount() == 1 ? message : (message + " (line " + e.line + ")"), true); - return null; - } - RootCallTarget callTarget = doMakeCallTarget(node.asRNode(), "<repl wrapper>"); - try { - return runCall(callTarget, frame, printResult, true); - } catch (BreakException | NextException cfe) { - throw RError.error(RError.NO_NODE, RError.Message.NO_LOOP_FOR_BREAK_NEXT); } } - public RExpression parse(Source source) throws RContext.Engine.ParseException { - try { - Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(source.getCode()), source); - ASTNode[] exprs = seq.getExpressions(); - Object[] data = new Object[exprs.length]; - for (int i = 0; i < exprs.length; i++) { - data[i] = RDataFactory.createLanguage(transform(exprs[i]).asRNode()); - } - return RDataFactory.createExpression(RDataFactory.createList(data)); - } catch (RecognitionException ex) { - throw new RContext.Engine.ParseException(ex, ex.getMessage()); - } + public RExpression parse(Source source) throws ParseException { + Sequence seq = (Sequence) parseImpl(source); + Object[] data = Arrays.stream(seq.getExpressions()).map(expr -> RDataFactory.createLanguage(transform(expr).asRNode())).toArray(); + return RDataFactory.createExpression(RDataFactory.createList(data)); } - public RFunction parseFunction(String name, Source source, MaterializedFrame enclosingFrame) throws RContext.Engine.ParseException { - try { - Sequence seq = (Sequence) ParseUtil.parseAST(new ANTLRStringStream(source.getCode()), source); - ASTNode[] exprs = seq.getExpressions(); - assert exprs.length == 1; + public RFunction parseFunction(String name, Source source, MaterializedFrame enclosingFrame) throws ParseException { + Sequence seq = (Sequence) parseImpl(source); + ASTNode[] exprs = seq.getExpressions(); + assert exprs.length == 1; - return new RTruffleVisitor().transformFunction(name, (Function) exprs[0], enclosingFrame); - } catch (RecognitionException ex) { - throw new RContext.Engine.ParseException(ex, ex.getMessage()); - } + return new RTruffleVisitor().transformFunction(name, (Function) exprs[0], enclosingFrame); } - public CallTarget parseToCallTarget(Source source) { - RSyntaxNode node; - try { - node = parseToRNode(source); - return new TVMCallTarget(doMakeCallTarget(node.asRNode(), "<tl parse>")); - } catch (RecognitionException ex) { - return null; - } + @Override + public CallTarget parseToCallTarget(Source source, boolean printResult) throws ParseException { + ASTNode ast = parseImpl(source); + return new TVMCallTarget(ast, printResult); } - private class TVMCallTarget implements RootCallTarget { - private RootCallTarget delegate; + private static class TVMCallTarget implements RootCallTarget { + + private final ASTNode ast; + private final boolean printResult; + + @SuppressWarnings("unchecked") @Child private FindContextNode<RContext> findContext = (FindContextNode<RContext>) TruffleRLanguage.INSTANCE.actuallyCreateFindContextNode(); - TVMCallTarget(RootCallTarget delegate) { - this.delegate = delegate; + TVMCallTarget(ASTNode ast, boolean printResult) { + this.ast = ast; + this.printResult = printResult; } @Override public Object call(Object... arguments) { - return runCall(delegate, globalFrame, true, true); + RSyntaxNode node = transform(ast); + RootCallTarget callTarget = doMakeCallTarget(node.asRNode(), "<repl wrapper>"); + + RContext oldContext = RContext.threadLocalContext.get(); + RContext context = findContext.executeFindContext(); + RContext.threadLocalContext.set(context); + try { + return ((REngine) context.getThisEngine()).runCall(callTarget, context.stateREnvironment.getGlobalFrame(), printResult, true); + } finally { + RContext.threadLocalContext.set(oldContext); + } } public RootNode getRootNode() { - return delegate.getRootNode(); + return null; } } - public Object eval(RExpression exprs, REnvironment envir, REnvironment enclos, int depth) throws PutException { + public Object eval(RExpression exprs, REnvironment envir, REnvironment enclos, int depth) { Object result = RNull.instance; for (int i = 0; i < exprs.getLength(); i++) { Object obj = RASTUtils.checkForRSymbol(exprs.getDataAt(i)); @@ -338,7 +315,7 @@ final class REngine implements RContext.Engine { return result; } - public Object eval(RLanguage expr, REnvironment envir, REnvironment enclos, int depth) throws PutException { + public Object eval(RLanguage expr, REnvironment envir, REnvironment enclos, int depth) { return evalNode((RNode) expr.getRep(), envir, enclos, depth); } @@ -402,18 +379,6 @@ final class REngine implements RContext.Engine { return runCall(closure.getCallTarget(), frame, false, false); } - /** - * Parses a text stream into a Truffle AST. - * - * @return the root node of the Truffle AST - * @throws RecognitionException on parse error - */ - private static RSyntaxNode parseToRNode(Source source) throws RecognitionException { - String code = source.getCode(); - RSyntaxNode result = transform(ParseUtil.parseAST(new ANTLRStringStream(code), source)); - return result; - } - /** * Transforms an AST produced by the parser into a Truffle AST. * @@ -468,18 +433,10 @@ final class REngine implements RContext.Engine { private Object runCall(RootCallTarget callTarget, MaterializedFrame frame, boolean printResult, boolean topLevel) { Object result = null; try { - try { - // FIXME: callTargets should only be called via Direct/IndirectCallNode - result = callTarget.call(frame); - } catch (ReturnException ex) { - // condition handling can cause a "return" that needs to skip over this call - throw ex; - } catch (BreakException | NextException cfe) { - // there can be an outer loop - throw cfe; - } + // FIXME: callTargets should only be called via Direct/IndirectCallNode + result = callTarget.call(frame); assert checkResult(result); - if (printResult) { + if (printResult && result != null) { assert topLevel; if (context.isVisible()) { printResult(result); @@ -491,9 +448,15 @@ final class REngine implements RContext.Engine { } catch (RError e) { throw e; } catch (ReturnException ex) { + // condition handling can cause a "return" that needs to skip over this call throw ex; } catch (BreakException | NextException cfe) { - throw cfe; + if (topLevel) { + throw RError.error(RError.NO_NODE, RError.Message.NO_LOOP_FOR_BREAK_NEXT); + } else { + // there can be an outer loop + throw cfe; + } } catch (UnsupportedSpecializationException use) { throw use; } catch (DebugExitException | QuitException | BrowserQuitException e) { @@ -513,7 +476,7 @@ final class REngine implements RContext.Engine { @TruffleBoundary private static boolean checkResult(Object result) { - if (FastROptions.CheckResultCompleteness.getValue() && result instanceof RAbstractVector && ((RAbstractVector) result).isComplete()) { + if (FastROptions.CheckResultCompleteness && result instanceof RAbstractVector && ((RAbstractVector) result).isComplete()) { assert ((RAbstractVector) result).checkCompleteness() : "vector: " + result + " is not complete, but isComplete flag is true"; } return true; @@ -524,20 +487,27 @@ final class REngine implements RContext.Engine { @TruffleBoundary public void printResult(Object result) { - Object resultValue = result instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) result) : result; - if (loadBase) { - Object printMethod = REnvironment.globalEnv().findFunction("print"); - RFunction function = (RFunction) (printMethod instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) printMethod) : printMethod); - if (FastROptions.NewStateTransition && resultValue instanceof RShareable) { - ((RShareable) resultValue).incRefCount(); - } - function.getTarget().call(RArguments.create(function, null, REnvironment.globalEnv().getFrame(), 1, new Object[]{resultValue, RMissing.instance}, PRINT_SIGNATURE, null)); - if (FastROptions.NewStateTransition && resultValue instanceof RShareable) { - ((RShareable) resultValue).decRefCount(); - } + // this supports printing of non-R values (via toString for now) + if (result instanceof TruffleObject && !(result instanceof RTypedValue)) { + RContext.getInstance().getConsoleHandler().println(String.valueOf(result)); + } else if (result instanceof CharSequence && !(result instanceof String)) { + RContext.getInstance().getConsoleHandler().println("\"" + String.valueOf(result) + "\""); } else { - // we only have the .Internal print.default method available - getPrintInternal().getTarget().call(RArguments.create(printInternal, null, REnvironment.globalEnv().getFrame(), 1, new Object[]{resultValue}, PRINT_INTERNAL_SIGNATURE, null)); + Object resultValue = result instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) result) : result; + if (loadBase) { + Object printMethod = REnvironment.globalEnv().findFunction("print"); + RFunction function = (RFunction) (printMethod instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) printMethod) : printMethod); + if (FastROptions.NewStateTransition && resultValue instanceof RShareable) { + ((RShareable) resultValue).incRefCount(); + } + function.getTarget().call(RArguments.create(function, null, REnvironment.globalEnv().getFrame(), 1, new Object[]{resultValue, RMissing.instance}, PRINT_SIGNATURE, null)); + if (FastROptions.NewStateTransition && resultValue instanceof RShareable) { + ((RShareable) resultValue).decRefCount(); + } + } else { + // we only have the .Internal print.default method available + getPrintInternal().getTarget().call(RArguments.create(printInternal, null, REnvironment.globalEnv().getFrame(), 1, new Object[]{resultValue}, PRINT_INTERNAL_SIGNATURE, null)); + } } } @@ -550,7 +520,7 @@ final class REngine implements RContext.Engine { try { RExpression funDef = parse(INTERNAL_PRINT); printInternal = (RFunction) eval(funDef, REnvironment.baseEnv().getFrame()); - } catch (RContext.Engine.ParseException ex) { + } catch (Engine.ParseException ex) { Utils.fail("failed to parse print.internal"); } } @@ -558,6 +528,10 @@ final class REngine implements RContext.Engine { } + public Class<? extends TruffleLanguage<RContext>> getTruffleLanguage() { + return TruffleRLanguage.class; + } + @TruffleBoundary private void reportImplementationError(Throwable e) { // R suicide, unless, e.g., we are running units tests. @@ -571,16 +545,13 @@ final class REngine implements RContext.Engine { } } - private void writeStderr(String s, boolean nl) { - try { - StdConnections.getStderr().writeString(s, nl); - } catch (IOException ex) { - // Very unlikely - ConsoleHandler consoleHandler = context.getConsoleHandler(); - consoleHandler.printErrorln("Error writing to stderr: " + ex.getMessage()); - consoleHandler.printErrorln(s); - + public ForeignAccess getForeignAccess(RTypedValue value) { + if (value instanceof RAbstractVector) { + return ForeignAccess.create(RAbstractVector.class, new RAbstractVectorAccessFactory()); + } else if (value instanceof RFunction) { + return ForeignAccess.create(RFunction.class, new RFunctionAccessFactory()); + } else { + throw RInternalError.shouldNotReachHere("cannot create ForeignAccess for " + value); } } - } 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 e27baf8395c759197377356a136fded6bf45ced8..f31b8311faf005bb68917c88a8c05d403844baec 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 @@ -25,6 +25,7 @@ package com.oracle.truffle.r.engine; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.vm.*; import com.oracle.truffle.r.nodes.*; import com.oracle.truffle.r.nodes.access.*; import com.oracle.truffle.r.nodes.access.array.read.*; @@ -35,9 +36,8 @@ import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.nodes.instrument.debug.*; import com.oracle.truffle.r.nodes.runtime.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.ConsoleHandler; -import com.oracle.truffle.r.runtime.RContext.Engine; import com.oracle.truffle.r.runtime.RDeparse.State; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; @@ -125,6 +125,8 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess { return baseResult; } else if (node instanceof AccessFieldNode) { return 3; + } else if (node instanceof ReplacementNode) { + return 3; } else { // TODO fill out assert false : node; @@ -219,6 +221,8 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess { default: assert false; } + } else if (node instanceof ReplacementNode) { + return RNull.instance; } else { // TODO fill out assert false; @@ -226,6 +230,31 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess { return null; } + public Object fromList(RAbstractVector list) { + int length = list.getLength(); + if (length == 0) { + return RNull.instance; + } else if (length == 1) { + return list.getDataAtAsObject(0); + } else { + RNode fn = unwrapToRNode(list.getDataAtAsObject(0)); + RSyntaxNode[] arguments = new RSyntaxNode[length - 1]; + for (int i = 1; i < length; i++) { + arguments[i - 1] = (RSyntaxNode) unwrapToRNode(list.getDataAtAsObject(i)); + } + return RDataFactory.createLanguage(RASTUtils.createCall(fn, ArgumentsSignature.empty(arguments.length), arguments).asRNode()); + } + } + + private static RNode unwrapToRNode(Object o) { + if (o instanceof RLanguage) { + return (RNode) RASTUtils.unwrap(((RLanguage) o).getRep()); + } else { + // o is RSymbol or a primitive value + return ConstantNode.create(o); + } + } + public RList asList(RLanguage rl) { Object[] data = new Object[getLength(rl)]; for (int i = 0; i < data.length; i++) { @@ -395,11 +424,10 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess { public void setFunctionName(RootNode node, String name) { ((FunctionDefinitionNode) node).setDescription(name); - } - public RContext create(RContext parent, RContext.Kind kind, String[] commandArgs, ConsoleHandler consoleHandler) { - return RContextFactory.create(parent, kind, commandArgs, consoleHandler); + public TruffleVM create(ContextInfo info) { + return RContextFactory.create(info); } public Engine createEngine(RContext context) { @@ -436,7 +464,7 @@ public class RRuntimeASTAccessImpl implements RRuntimeASTAccess { * {@code call == null}, it's pretty simple as we just back off to the frame, where the call * will have been stored. Otherwise, we have to deal with the different ways in which the * internal implementation can generate an error/warning and locate the correct node, and try to - * maych the behavior of GnuR regarding {@code .Internal} (TODO). + * match the behavior of GnuR regarding {@code .Internal} (TODO). */ public Object findCaller(Node call) { Frame frame = Utils.getActualCurrentFrame(); diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java index 433577d9810597aa94ab1b2a672288d2e4a3a1a7..822ad7d45b002ecc1d5577de40c5e82a4004b49b 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java @@ -30,19 +30,21 @@ import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.engine.repl.debug.*; -import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; /** * Only does the minimum for running under the debugger. It is not completely clear how to correctly * integrate the R startup in {@code RCommand} with this API. */ -@TruffleLanguage.Registration(name = "R", version = "3.1.3", mimeType = "application/x-r") +@TruffleLanguage.Registration(name = "R", version = "0.1", mimeType = {"application/x-r", "text/x-r"}) public final class TruffleRLanguage extends TruffleLanguage<RContext> { private DebugSupportProvider debugSupport; public static final TruffleRLanguage INSTANCE = new TruffleRLanguage(); + public static final String MIME = "application/x-r"; + private TruffleRLanguage() { } @@ -66,12 +68,7 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> { @Override protected RContext createContext(Env env) { - /* - * TODO we assume here that the initial context has already been created, which is certainly - * true by fiat when running under the debugger, but may not be in general. - */ - RContext result = RContext.getInstance(); - return result; + return RContext.create(env); } @Override @@ -82,17 +79,22 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> { * TruffleVM does not know about, we have to use a delegation mechanism via a wrapper * CallTarget class, using a special REngine entry point. */ - return RContext.getEngine().parseToCallTarget(source); + return RContext.getEngine().parseToCallTarget(source, true); } @Override protected Object findExportedSymbol(RContext context, String globalName, boolean onlyExplicit) { - throw RInternalError.unimplemented("findExportedSymbol"); + return context.getExportedSymbols().get(globalName); } @Override protected Object getLanguageGlobal(RContext context) { - throw RInternalError.unimplemented("getLanguageGlobal"); + // TODO: what's the meaning of "language global" for R? + return null; } + // TODO: why isn't the original method public? + public Node actuallyCreateFindContextNode() { + return createFindContextNode(); + } } diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ConstantRootNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ConstantRootNode.java new file mode 100644 index 0000000000000000000000000000000000000000..05a4ead0e543af2a660e8ce2da44816c6d3d4d2f --- /dev/null +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ConstantRootNode.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.engine.interop; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.r.engine.*; +import com.oracle.truffle.r.nodes.access.*; + +public class ConstantRootNode extends RootNode { + + @Child private ConstantNode constant; + + public ConstantRootNode(ConstantNode constant) { + super(TruffleRLanguage.class, null, null); + this.constant = constant; + } + + @Override + public Object execute(VirtualFrame frame) { + return constant.execute(frame); + } +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..31538e041e0ae7a421eaa9ad801cab12ee18ce99 --- /dev/null +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.engine.interop; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.api.interop.ForeignAccess.Factory10; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.r.engine.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.model.*; + +public final class RAbstractVectorAccessFactory implements Factory10 { + + public abstract class InteropRootNode extends RootNode { + public InteropRootNode() { + super(TruffleRLanguage.class, null, null); + } + } + + public CallTarget accessIsNull() { + throw RInternalError.shouldNotReachHere("message: accessIsNull"); + } + + public CallTarget accessIsExecutable() { + throw RInternalError.shouldNotReachHere("message: accessIsExecutable"); + } + + public CallTarget accessIsBoxed() { + return Truffle.getRuntime().createCallTarget(new InteropRootNode() { + @Override + public Object execute(VirtualFrame frame) { + RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame); + return arg.getLength() == 1; + } + }); + } + + public CallTarget accessHasSize() { + return Truffle.getRuntime().createCallTarget(new InteropRootNode() { + @Override + public Object execute(VirtualFrame frame) { + return true; + } + }); + } + + public CallTarget accessGetSize() { + return Truffle.getRuntime().createCallTarget(new VectorSizeNode()); + } + + public CallTarget accessUnbox() { + return Truffle.getRuntime().createCallTarget(new InteropRootNode() { + @Override + public Object execute(VirtualFrame frame) { + RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame); + return arg.getDataAtAsObject(0); + } + }); + } + + public CallTarget accessRead() { + return Truffle.getRuntime().createCallTarget(new VectorReadNode()); + } + + public CallTarget accessWrite() { + throw RInternalError.shouldNotReachHere("message: accessWrite"); + } + + public CallTarget accessExecute(int argumentsLength) { + throw RInternalError.shouldNotReachHere("message: accessExecute"); + } + + public CallTarget accessInvoke(int argumentsLength) { + throw RInternalError.shouldNotReachHere("message: accessInvoke"); + } + + public CallTarget accessMessage(Message unknown) { + throw RInternalError.shouldNotReachHere("message: " + unknown); + } +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionAccessFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionAccessFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..4e40bd5c34184a0b6ea6a4cc8ca132b12557b100 --- /dev/null +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RFunctionAccessFactory.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.engine.interop; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.interop.ForeignAccess.Factory10; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.r.nodes.access.*; +import com.oracle.truffle.r.runtime.*; + +public final class RFunctionAccessFactory implements Factory10 { + + public CallTarget accessIsNull() { + throw RInternalError.shouldNotReachHere("message: accessIsNull"); + } + + public CallTarget accessIsExecutable() { + return Truffle.getRuntime().createCallTarget(new ConstantRootNode(ConstantNode.create(true))); + } + + public CallTarget accessIsBoxed() { + throw RInternalError.shouldNotReachHere("message: accessIsBoxed"); + } + + public CallTarget accessHasSize() { + return Truffle.getRuntime().createCallTarget(new ConstantRootNode(ConstantNode.create(false))); + } + + public CallTarget accessGetSize() { + throw RInternalError.shouldNotReachHere("message: accessIsBoxed"); + } + + public CallTarget accessUnbox() { + throw RInternalError.shouldNotReachHere("message: accessUnbox"); + } + + public CallTarget accessRead() { + throw RInternalError.shouldNotReachHere("message: accessIsBoxed"); + } + + public CallTarget accessWrite() { + throw RInternalError.shouldNotReachHere("message: accessWrite"); + } + + public CallTarget accessExecute(int argumentsLength) { + return Truffle.getRuntime().createCallTarget(new RInteropExecuteNode(argumentsLength)); + } + + public CallTarget accessInvoke(int argumentsLength) { + throw RInternalError.shouldNotReachHere("message: accessInvoke"); + } + + public CallTarget accessMessage(Message unknown) { + throw RInternalError.shouldNotReachHere("message: " + unknown); + } +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropExecuteNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropExecuteNode.java new file mode 100644 index 0000000000000000000000000000000000000000..7e5b652bca5a2f88469e24f01d777fbd196e80ea --- /dev/null +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RInteropExecuteNode.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.engine.interop; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.r.engine.*; +import com.oracle.truffle.r.nodes.function.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; + +public class RInteropExecuteNode extends RootNode { + + private static final FrameDescriptor emptyFrameDescriptor = new FrameDescriptor(); + + @Child CallMatcherNode callMatcher = CallMatcherNode.create(false, true); + + private final ArgumentsSignature suppliedSignature; + + public RInteropExecuteNode(int argumentsLength) { + super(TruffleRLanguage.class, null, null); + suppliedSignature = ArgumentsSignature.empty(argumentsLength); + } + + @Override + public Object execute(VirtualFrame frame) { + RFunction function = (RFunction) ForeignAccess.getReceiver(frame); + List<Object> arguments = ForeignAccess.getArguments(frame); + + Object[] dummyFrameArgs = RArguments.createUnitialized(); + VirtualFrame dummyFrame = Truffle.getRuntime().createVirtualFrame(dummyFrameArgs, emptyFrameDescriptor); + + return callMatcher.execute(dummyFrame, suppliedSignature, arguments.toArray(), function, null); + } +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorReadNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorReadNode.java new file mode 100644 index 0000000000000000000000000000000000000000..e64d7932d6017199840691066ecf43ee0c018fae --- /dev/null +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorReadNode.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.engine.interop; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.engine.*; +import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctions.AccessArraySubscriptBuiltin; +import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctionsFactory.AccessArraySubscriptBuiltinNodeGen; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +public class VectorReadNode extends RootNode { + + @CompilationFinal private boolean lengthAccess; + @Child private AccessArraySubscriptBuiltin builtin; + + private final BranchProfile intIndex = BranchProfile.create(); + private final BranchProfile doubleIndex = BranchProfile.create(); + private final BranchProfile longIndex = BranchProfile.create(); + + public VectorReadNode() { + super(TruffleRLanguage.class, null, null); + this.lengthAccess = false; + } + + @Override + public Object execute(VirtualFrame frame) { + Object label = ForeignAccess.getArguments(frame).get(0); + if (lengthAccess && !(label instanceof String)) { + CompilerDirectives.transferToInterpreter(); + lengthAccess = false; + } + if (!lengthAccess && (label instanceof String)) { + CompilerDirectives.transferToInterpreter(); + lengthAccess = true; + } + + RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame); + if (lengthAccess) { + if (label.equals("length")) { + return arg.getLength(); + } else { + CompilerDirectives.transferToInterpreter(); + throw RInternalError.shouldNotReachHere("unknown message: " + label); + } + } else { + if (builtin == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + builtin = insert(AccessArraySubscriptBuiltinNodeGen.create(null, null, null)); + } + int index; + if (label instanceof Integer) { + intIndex.enter(); + index = (int) label; + } else if (label instanceof Long) { + longIndex.enter(); + index = (int) (long) label; + } else if (label instanceof Double) { + doubleIndex.enter(); + index = (int) (double) label; + } else { + CompilerDirectives.transferToInterpreter(); + throw RInternalError.shouldNotReachHere("invalid index type: " + label); + } + return builtin.execute(frame, arg, new RArgsValuesAndNames(new Object[]{index + 1}, ArgumentsSignature.empty(1)), RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_TRUE); + } + } + +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorSizeNode.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorSizeNode.java new file mode 100644 index 0000000000000000000000000000000000000000..15dcb8866128d893b8c307a05a9a654236aa2f91 --- /dev/null +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/VectorSizeNode.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.engine.interop; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.r.engine.*; +import com.oracle.truffle.r.runtime.data.model.*; + +public class VectorSizeNode extends RootNode { + + public VectorSizeNode() { + super(TruffleRLanguage.class, null, null); + } + + @Override + public Object execute(VirtualFrame frame) { + RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame); + return arg.getLength(); + } +} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/RREPLServer.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/RREPLServer.java index d98abbacc807ed6bce82350dbce07e3443121862..e8bf544eb3eaf7b325fed32178fbe2ed1912d1ba 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/RREPLServer.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/RREPLServer.java @@ -29,8 +29,10 @@ import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.vm.*; import com.oracle.truffle.api.vm.TruffleVM.Language; +import com.oracle.truffle.r.engine.*; import com.oracle.truffle.r.engine.shell.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.tools.debug.shell.*; import com.oracle.truffle.tools.debug.shell.client.*; import com.oracle.truffle.tools.debug.shell.server.*; @@ -118,13 +120,11 @@ public final class RREPLServer extends REPLServer { String[] debugArgs = new String[args.length + 1]; debugArgs[0] = "--debugger=rrepl"; System.arraycopy(args, 0, debugArgs, 1, args.length); - RContext initialContext = RCommand.debuggerMain(debugArgs); - initialContext.getThisEngine().getTruffleVMBuilder().onEvent(onHalted).onEvent(onExec); - // This actually "builds" the TruffleVM - initialContext.activate(); - this.vm = initialContext.getThisEngine().getTruffleVM(); - assert vm != null; - this.language = vm.getLanguages().get("application/x-r"); + RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args); + ContextInfo info = RCommand.createContextInfoFromCommandLine(options); + RContext.tempInitializingContextInfo = info; + this.vm = RContextFactory.create(info, builder -> builder.onEvent(onHalted).onEvent(onExec)); + this.language = vm.getLanguages().get(TruffleRLanguage.MIME); assert language != null; this.statusPrefix = language.getShortName() + " REPL:"; diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/debug/RDebugSupportProvider.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/debug/RDebugSupportProvider.java index f7792862adb2a845041740a8deb53deae7b8c0e0..511df46026f5d4e5c81eeb5d02e0ec56c705890e 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/debug/RDebugSupportProvider.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/repl/debug/RDebugSupportProvider.java @@ -31,8 +31,9 @@ import com.oracle.truffle.api.instrument.impl.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.Engine.ParseException; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.nodes.*; @@ -43,7 +44,11 @@ public final class RDebugSupportProvider implements DebugSupportProvider { @Override public Object evalInContext(Source source, Node node, MaterializedFrame frame) { - return RContext.getEngine().parseAndEval(source, frame, false, false); + try { + return RContext.getEngine().parseAndEval(source, frame, false); + } catch (ParseException e) { + throw new RInternalError(e, "error while parsing debug statement from %s", source.getName()); + } } @Override diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java index 2373cf2e38c123a1a5f1358a46f208ab9ddc59a9..2909ff8411d51d2c90f74b7186b77efc2e4a3d03 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java @@ -26,10 +26,11 @@ import java.io.*; import jline.console.*; -import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; -public class JLineConsoleHandler implements RContext.ConsoleHandler { +public class JLineConsoleHandler implements ConsoleHandler { private final ConsoleReader console; private final boolean isInteractive; private final PrintWriter printWriter; @@ -98,4 +99,8 @@ public class JLineConsoleHandler implements RContext.ConsoleHandler { public int getWidth() { return RContext.CONSOLE_WIDTH; } + + public String getInputDescription() { + return "<shell_input>"; + } } diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCmdOptionsParser.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCmdOptionsParser.java deleted file mode 100644 index 9ae976eb8cc446f8d49ae35aa39256efa25ca8f9..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCmdOptionsParser.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.engine.shell; - -import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RCmdOptions.Client; -import com.oracle.truffle.r.runtime.RCmdOptions.Option; -import com.oracle.truffle.r.runtime.RCmdOptions.OptionType; - -/** - * Implements the standard R/Rscript command line syntax. - * - * R supports {@code --arg=value} or {@code -arg value} for string-valued options. - * - * The spec for {@code commandArgs()} states that it returns the executable by which R was invoked - * in element 0, which is consistent with the C {@code main} function, but defines the exact form to - * be platform independent. Java does not provide the executable (for obvious reasons) so we use - * "FastR". - */ -public class RCmdOptionsParser { - // CheckStyle: stop system..print check - public static class Result { - /** - * The original {@code args} array, with element zero set to "FastR". - */ - public final String[] args; - /** - * Index in {@code args} of the first non-option argument or {@code args.length} if none. - */ - public final int firstNonOptionArgIndex; - - Result(String[] args, int firstNonOptionArgIndex) { - this.args = args; - this.firstNonOptionArgIndex = firstNonOptionArgIndex; - } - } - - /** - * Parse the arguments, setting the corresponding {@code Option values}. - */ - public static Result parseArguments(Client client, String[] args) { - RCmdOptions.reset(); - int i = 0; - int firstNonOptionArgIndex = -1; - while (i < args.length) { - final String arg = args[i]; - Option<?> option = RCmdOptions.matchOption(arg); - if (option == null) { - // for Rscript, this means we are done - if (client == Client.RSCRIPT) { - firstNonOptionArgIndex = i; - break; - } - // GnuR does not abort, simply issues a warning - System.out.printf("WARNING: unknown option '%s'%n", arg); - i++; - continue; - } else if (option.matchedShort() && i == args.length - 1) { - System.out.println("usage:"); - printHelp(client, 1); - } - // check implemented - if (!option.implemented) { - System.out.println("WARNING: option: " + arg + " is not implemented"); - } - if (option.matchedShort()) { - i++; - option.setValue(args[i]); - } else { - if (option.type == OptionType.BOOLEAN) { - option.setValue(true); - } else if (option.type == OptionType.STRING) { - int eqx = arg.indexOf('='); - option.setValue(arg.substring(eqx + 1)); - } - } - i++; - // check for --args, in which case stop parsing - if (option == RCmdOptions.ARGS) { - firstNonOptionArgIndex = i; - break; - } - } - String[] xargs = new String[args.length + 1]; - xargs[0] = "FastR"; - System.arraycopy(args, 0, xargs, 1, args.length); - return new Result(xargs, firstNonOptionArgIndex < 0 ? xargs.length : firstNonOptionArgIndex + 1); // adj - // for zeroth arg insert - } - - public static void printHelp(Client client, int exitCode) { - System.out.println(client.usage()); - System.out.println("Options:"); - for (Option<?> option : RCmdOptions.optionList()) { - System.out.printf(" %-22s %s%n", option.getHelpName(), option.help); - } - System.out.println("\nFILE may contain spaces but not shell metacharacters.\n"); - if (exitCode >= 0) { - System.exit(exitCode); - } - } - -} diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java index 99444a4c394ca9dbff36dc83922b4a616a110b25..f3fed0c1701c131acfda1ff1dc08b361f22fadc5 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java @@ -22,7 +22,7 @@ */ package com.oracle.truffle.r.engine.shell; -import static com.oracle.truffle.r.runtime.RCmdOptions.*; +import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.*; import java.io.*; import java.nio.file.*; @@ -30,14 +30,17 @@ import java.util.*; import jline.console.*; -import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.vm.*; import com.oracle.truffle.r.engine.*; import com.oracle.truffle.r.nodes.builtin.base.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.ConsoleHandler; -import com.oracle.truffle.r.runtime.RContext.Engine; +import com.oracle.truffle.r.runtime.context.*; +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.ContextKind; import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; /** * Emulates the (Gnu)R command as precisely as possible. @@ -47,65 +50,34 @@ public class RCommand { // CheckStyle: stop system..print check public static void main(String[] args) { - internalMain(args, true); + RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args); + options.printHelpAndVersion(); + ContextInfo info = createContextInfoFromCommandLine(options); + // never returns + readEvalPrint(info); + throw RInternalError.shouldNotReachHere(); } - public static RContext internalMain(String[] args, boolean eval) { - try { - RCmdOptionsParser.Result result = RCmdOptionsParser.parseArguments(RCmdOptions.Client.R, args); - if (HELP.getValue()) { - RCmdOptionsParser.printHelp(RCmdOptions.Client.R, 0); - } else if (VERSION.getValue()) { - printVersionAndExit(); - } else if (RHOME.getValue()) { - printRHomeAndExit(); - } - return subMainInit(result.args, eval); - } catch (Utils.DebugExitException ex) { - /* - * This is thrown instead of doing System.exit, when we are running under the in-process - * Truffle debugger. We just return to the debugger command loop, possibly to be - * re-entered with a new evaluation. - */ - return null; - } catch (QuitException ex) { - /* This is thrown by the Truffle debugger when the user executes the 'q' command. */ - return null; + public static ContextInfo createContextInfoFromCommandLine(RCmdOptions options) { + if (options.getBoolean(SLAVE)) { + options.setValue(QUIET, true); + options.setValue(NO_SAVE, true); } - } - - public static RContext debuggerMain(String[] args) { - return internalMain(args, false); - } - /** - * Entry point for {@link RscriptCommand} avoiding re-parsing. - */ - public static void rscriptMain(String[] args) { - subMainInit(args, true); - } - - public static RContext subMainInit(String[] args, boolean eval) { - - if (SLAVE.getValue()) { - QUIET.setValue(true); - NO_SAVE.setValue(true); + if (options.getBoolean(VANILLA)) { + options.setValue(NO_SAVE, true); + options.setValue(NO_ENVIRON, true); + options.setValue(NO_INIT_FILE, true); + options.setValue(NO_RESTORE, true); } - if (VANILLA.getValue()) { - NO_SAVE.setValue(true); - NO_ENVIRON.setValue(true); - NO_INIT_FILE.setValue(true); - NO_RESTORE.setValue(true); - } - - String fileArg = FILE.getValue(); + String fileArg = options.getString(FILE); if (fileArg != null) { - if (EXPR.getValue() != null) { + if (options.getStringList(EXPR) != null) { Utils.fatalError("cannot use -e with -f or --file"); } - if (!SAVE.getValue()) { - NO_SAVE.setValue(true); + if (!options.getBoolean(SLAVE)) { + options.setValue(NO_SAVE, true); } if (fileArg.equals("-")) { // means stdin, but still implies NO_SAVE @@ -113,9 +85,7 @@ public class RCommand { } } - RContextFactory.initialize(); - - if (!(QUIET.getValue() || SILENT.getValue())) { + if (!(options.getBoolean(QUIET) || options.getBoolean(SILENT))) { System.out.println(RRuntime.WELCOME_MESSAGE); } /* @@ -126,9 +96,9 @@ public class RCommand { ConsoleHandler consoleHandler; InputStream consoleInput = System.in; OutputStream consoleOutput = System.out; - String filePath = null; if (fileArg != null) { List<String> lines; + String filePath; try { File file = new File(fileArg); lines = Files.readAllLines(file.toPath()); @@ -136,13 +106,13 @@ public class RCommand { } catch (IOException e) { throw Utils.fatalError("cannot open file '" + fileArg + "': " + e.getMessage()); } - consoleHandler = new StringConsoleHandler(lines, System.out); - } else if (EXPR.getValue() != null) { - List<String> exprs = EXPR.getValue(); - if (!SAVE.getValue()) { - NO_SAVE.setValue(true); + consoleHandler = new StringConsoleHandler(lines, System.out, filePath); + } else if (options.getStringList(EXPR) != null) { + List<String> exprs = options.getStringList(EXPR); + if (!options.getBoolean(SLAVE)) { + options.setValue(NO_SAVE, true); } - consoleHandler = new StringConsoleHandler(exprs, System.out); + consoleHandler = new StringConsoleHandler(exprs, System.out, "<expression input>"); } else { /* * GnuR behavior differs from the manual entry for {@code interactive} in that {@code @@ -158,37 +128,18 @@ public class RCommand { } catch (IOException ex) { throw Utils.fail("unexpected error opening console reader"); } - boolean isInteractive = INTERACTIVE.getValue() || sysConsole != null; - if (!isInteractive && !SAVE.getValue() && !NO_SAVE.getValue() && !VANILLA.getValue()) { + boolean isInteractive = options.getBoolean(INTERACTIVE) || sysConsole != null; + if (!isInteractive && !options.getBoolean(SAVE) && !options.getBoolean(NO_SAVE) && !options.getBoolean(VANILLA)) { throw Utils.fatalError("you must specify '--save', '--no-save' or '--vanilla'"); } // long start = System.currentTimeMillis(); consoleHandler = new JLineConsoleHandler(isInteractive, consoleReader); } - RContext context = RContextFactory.createInitial(args, consoleHandler); - if (eval) { - // never returns - context.activate(); - readEvalPrint(consoleHandler, context, filePath); - throw RInternalError.shouldNotReachHere(); - } else { - return context; - } - } - - private static void printVersionAndExit() { - System.out.print("FastR version "); - System.out.println(RVersionNumber.FULL); - System.out.println(RRuntime.LICENSE); - Utils.exit(0); - } - - private static void printRHomeAndExit() { - System.out.println(REnvVars.rHome()); - throw Utils.exit(0); + return ContextInfo.create(options, ContextKind.SHARE_NOTHING, null, consoleHandler); } - private static final Source QUIT_EOF = Source.fromText("quit(\"default\", 0L, TRUE)", "<quit_file>"); + private static final String GET_ECHO = "invisible(getOption('echo'))"; + private static final String QUIT_EOF = "quit(\"default\", 0L, TRUE)"; /** * The read-eval-print loop, which can take input from a console, command line expression or a @@ -201,13 +152,14 @@ public class RCommand { * In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before * exiting. So,in either case, we never return. */ - private static void readEvalPrint(ConsoleHandler consoleHandler, RContext context, String filePath) { - String inputDescription = filePath == null ? "<shell_input>" : filePath; - Source source = Source.fromNamedAppendableText(inputDescription); + public static void readEvalPrint(ContextInfo info) { + TruffleVM vm = RContextFactory.create(info); + ConsoleHandler consoleHandler = info.getConsoleHandler(); + Source source = Source.fromNamedAppendableText(consoleHandler.getInputDescription()); try { // console.println("initialize time: " + (System.currentTimeMillis() - start)); - for (;;) { - boolean doEcho = doEcho(); + REPL: for (;;) { + boolean doEcho = doEcho(vm); consoleHandler.setPrompt(doEcho ? "> " : null); try { String input = consoleHandler.readLine(); @@ -227,7 +179,28 @@ public class RCommand { try { String continuePrompt = getContinuePrompt(); Source subSource = Source.subSource(source, startLength); - while (context.getThisEngine().parseAndEval(subSource, true, true) == Engine.INCOMPLETE_SOURCE) { + while (true) { + try { + // TODO: how to pass subSource to TruffleVM? + vm.eval(TruffleRLanguage.MIME, subSource.getCode()); + continue REPL; + } catch (IncompleteSourceException e) { + // read another line of input + } catch (ParseException e) { + try { + throw e.throwAsRError(); + } catch (RError e2) { + // this error is expected + } + continue REPL; + } catch (IOException e) { + if (e.getCause() instanceof RError) { + // nothing to do + } else { + RInternalError.reportError(e); + } + continue REPL; + } consoleHandler.setPrompt(doEcho ? continuePrompt : null); String additionalInput = consoleHandler.readLine(); if (additionalInput == null) { @@ -246,22 +219,34 @@ public class RCommand { } catch (BrowserQuitException e) { // can happen if user profile invokes browser } catch (EOFException ex) { - context.getThisEngine().parseAndEval(QUIT_EOF, false, false); + try { + vm.eval(TruffleRLanguage.MIME, QUIT_EOF); + } catch (IOException e) { + throw RInternalError.shouldNotReachHere(e); + } } finally { - context.destroy(); + RContext.destroyContext(vm); } } - private static boolean doEcho() { - if (SLAVE.getValue()) { - return false; + private static boolean doEcho(TruffleVM vm) { + Object echo; + try { + echo = vm.eval(TruffleRLanguage.MIME, GET_ECHO); + } catch (IOException e) { + throw RInternalError.shouldNotReachHere(e); + } + if (echo instanceof Byte) { + return RRuntime.fromLogical((Byte) echo); + } else if (echo instanceof RAbstractLogicalVector) { + return RRuntime.fromLogical(((RAbstractLogicalVector) echo).getDataAt(0)); + } else { + throw RInternalError.shouldNotReachHere(); } - RLogicalVector echo = (RLogicalVector) RRuntime.asAbstractVector(RContext.getROptionsState().getValue("echo")); - return RRuntime.fromLogical(echo.getDataAt(0)); } private static String getContinuePrompt() { - RStringVector continuePrompt = (RStringVector) RRuntime.asAbstractVector(RContext.getROptionsState().getValue("continue")); + RStringVector continuePrompt = (RStringVector) RRuntime.asAbstractVector(RContext.getInstance().stateROptions.getValue("continue")); return continuePrompt.getDataAt(0); } diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java index 64ce406ebeb3979e5fd2d7e8290eee2790a868d9..25c2493fb47a242e01d59000863c8affd1a28b3c 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java @@ -22,11 +22,13 @@ */ package com.oracle.truffle.r.engine.shell; -import static com.oracle.truffle.r.runtime.RCmdOptions.*; +import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.*; import java.util.*; +import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; /** * Emulates the (Gnu)Rscript command as precisely as possible. in GnuR, Rscript is a genuine wrapper @@ -37,43 +39,41 @@ import com.oracle.truffle.r.runtime.*; */ public class RscriptCommand { // CheckStyle: stop system..print check - public static void main(String[] args) { - // Since many of the options are shared parse them from an RSCRIPT perspective. - // Handle --help and --version specially, as they exit. - RCmdOptionsParser.Result result = RCmdOptionsParser.parseArguments(RCmdOptions.Client.RSCRIPT, args); - int resultArgsLength = result.args.length; - int firstNonOptionArgIndex = result.firstNonOptionArgIndex; - if (HELP.getValue()) { - RCmdOptionsParser.printHelp(RCmdOptions.Client.RSCRIPT, 0); + + private static void preprocessRScriptOptions(RCmdOptions options) { + String[] arguments = options.getArguments(); + int resultArgsLength = arguments.length; + int firstNonOptionArgIndex = options.getFirstNonOptionArgIndex(); + if (options.getBoolean(HELP)) { + RCmdOptions.printHelp(RCmdOptions.Client.RSCRIPT, 0); Utils.exit(0); - } else if (VERSION.getValue()) { + } else if (options.getBoolean(VERSION)) { printVersionAndExit(); } // Now reformat the args, setting --slave and --no-restore as per the spec - // and invoke RCommand.subMain ArrayList<String> adjArgs = new ArrayList<>(resultArgsLength + 1); - adjArgs.add(result.args[0]); + adjArgs.add(arguments[0]); adjArgs.add("--slave"); - SLAVE.setValue(true); + options.setValue(SLAVE, true); adjArgs.add("--no-restore"); - NO_RESTORE.setValue(true); + options.setValue(NO_RESTORE, true); // Either -e options are set or first non-option arg is a file - if (EXPR.getValue() == null) { + if (options.getStringList(EXPR) == null) { if (firstNonOptionArgIndex == resultArgsLength) { System.err.println("filename is missing"); Utils.exit(2); } else { - FILE.setValue(result.args[firstNonOptionArgIndex]); + options.setValue(FILE, arguments[firstNonOptionArgIndex]); } } // copy up to non-option args int rx = 1; while (rx < firstNonOptionArgIndex) { - adjArgs.add(result.args[rx]); + adjArgs.add(arguments[rx]); rx++; } - if (FILE.getValue() != null) { - adjArgs.add("--file=" + FILE.getValue()); + if (options.getString(FILE) != null) { + adjArgs.add("--file=" + options.getString(FILE)); rx++; // skip over file arg firstNonOptionArgIndex++; } @@ -81,12 +81,29 @@ public class RscriptCommand { if (firstNonOptionArgIndex < resultArgsLength) { adjArgs.add("--args"); while (rx < resultArgsLength) { - adjArgs.add(result.args[rx++]); + adjArgs.add(arguments[rx++]); } } - String[] adjArgsArray = new String[adjArgs.size()]; - adjArgs.toArray(adjArgsArray); - RCommand.rscriptMain(adjArgsArray); + options.setArguments(adjArgs.toArray(new String[adjArgs.size()])); + } + + public static void main(String[] args) { + // Since many of the options are shared parse them from an RSCRIPT perspective. + // Handle --help and --version specially, as they exit. + RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.RSCRIPT, args); + preprocessRScriptOptions(options); + ContextInfo info = RCommand.createContextInfoFromCommandLine(options); + try { + RCommand.readEvalPrint(info); + } catch (Utils.DebugExitException ex) { + /* + * This is thrown instead of doing System.exit, when we are running under the in-process + * Truffle debugger. We just return to the debugger command loop, possibly to be + * re-entered with a new evaluation. + */ + } catch (QuitException ex) { + /* This is thrown by the Truffle debugger when the user executes the 'q' command. */ + } } private static void printVersionAndExit() { @@ -94,5 +111,4 @@ public class RscriptCommand { System.out.println(RVersionNumber.FULL); Utils.exit(0); } - } diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java index 0f04aa247f81ecf78b311862dbc0272c3fa8ae7f..2a026cd1cddc445833b1ff5f5ce3960be7d14016 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java @@ -26,17 +26,19 @@ import java.io.*; import java.util.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; -public class StringConsoleHandler implements RContext.ConsoleHandler { +public class StringConsoleHandler implements ConsoleHandler { private final PrintStream output; private final List<String> lines; + private final String inputDescription; private String prompt; private int currentLine; - public StringConsoleHandler(List<String> lines, PrintStream output) { + public StringConsoleHandler(List<String> lines, PrintStream output, String inputDescription) { this.lines = lines; this.output = output; + this.inputDescription = inputDescription; } @TruffleBoundary @@ -94,4 +96,8 @@ public class StringConsoleHandler implements RContext.ConsoleHandler { public int getWidth() { return RContext.CONSOLE_WIDTH; } + + public String getInputDescription() { + return inputDescription; + } } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java index 83a6bc602be0e0d9f598fb7280b999b2a706ca63..c7de1cb42b1b4b06032462b5812803bc46457299 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRContext.java @@ -24,11 +24,15 @@ package com.oracle.truffle.r.library.fastr; import java.io.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.vm.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RCmdOptions.Client; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.nodes.*; @@ -37,27 +41,29 @@ public class FastRContext { public abstract static class Create extends RExternalBuiltinNode.Arg2 { @Specialization + @TruffleBoundary protected int create(RAbstractStringVector args, RIntVector kindVec) { - String[] argsArray = args.getLength() == 1 ? new String[]{args.getDataAt(0)} : ((RStringVector) args).getDataCopy(); - RContext current = RContext.getInstance(); - RContext.ConsoleHandler consoleHandler = current.getConsoleHandler(); - RContext.Kind kind = RContext.Kind.values()[kindVec.getDataAt(0) - 1]; - RContext newContext = RContext.getRRuntimeASTAccess().create(current, kind, argsArray, consoleHandler); - return (int) newContext.getId(); + RContext.ContextKind kind = RContext.ContextKind.VALUES[kindVec.getDataAt(0) - 1]; + RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, args.materialize().getDataCopy()); + return ContextInfo.createDeferred(options, kind, RContext.getInstance(), RContext.getInstance().getConsoleHandler()); } } public abstract static class Print extends RExternalBuiltinNode.Arg1 { @Specialization + @TruffleBoundary protected RNull print(RAbstractIntVector ctxt) { if (ctxt.getLength() != 1) { throw RError.error(this, RError.Message.INVALID_ARGUMENT, "context"); } int contextId = ctxt.getDataAt(0); - @SuppressWarnings("unused") - RContext context = checkContext(contextId, this); + ContextInfo info = ContextInfo.get(contextId); try { - StdConnections.getStdout().writeString("context: " + contextId, true); + if (info == null) { + StdConnections.getStdout().writeString("obsolete context: " + contextId, true); + } else { + StdConnections.getStdout().writeString("context: " + contextId, true); + } return RNull.instance; } catch (IOException ex) { throw RError.error(this, RError.Message.GENERIC, ex.getMessage()); @@ -67,11 +73,12 @@ public class FastRContext { public abstract static class Spawn extends RExternalBuiltinNode.Arg2 { @Specialization + @TruffleBoundary protected RNull eval(RIntVector contexts, RAbstractStringVector exprs) { RContext.EvalThread[] threads = new RContext.EvalThread[contexts.getLength()]; for (int i = 0; i < threads.length; i++) { - RContext context = checkContext(contexts.getDataAt(i), this); - threads[i] = new RContext.EvalThread(context, Source.fromText(exprs.getDataAt(i % threads.length), "<context_eval>")); + ContextInfo info = checkContext(contexts.getDataAt(i), this); + threads[i] = new RContext.EvalThread(info, Source.fromText(exprs.getDataAt(i % threads.length), "<context_eval>")); } for (int i = 0; i < threads.length; i++) { threads[i].start(); @@ -85,12 +92,12 @@ public class FastRContext { protected RNull eval(RIntVector contexts) { try { for (int i = 0; i < contexts.getLength(); i++) { - RContext context = RContext.find(contexts.getDataAt(i)); - if (context == null) { + Thread thread = RContext.EvalThread.threads.get(contexts.getDataAt(i)); + if (thread == null) { // already done continue; } else { - context.joinThread(); + thread.join(); } } } catch (InterruptedException ex) { @@ -103,12 +110,13 @@ public class FastRContext { public abstract static class Eval extends RExternalBuiltinNode.Arg3 { @Specialization + @TruffleBoundary protected RNull eval(RIntVector contexts, RAbstractStringVector exprs, byte par) { if (RRuntime.fromLogical(par)) { RContext.EvalThread[] threads = new RContext.EvalThread[contexts.getLength()]; for (int i = 0; i < threads.length; i++) { - RContext context = checkContext(contexts.getDataAt(i), this); - threads[i] = new RContext.EvalThread(context, Source.fromText(exprs.getDataAt(i % threads.length), "<context_eval>")); + ContextInfo info = checkContext(contexts.getDataAt(i), this); + threads[i] = new RContext.EvalThread(info, Source.fromText(exprs.getDataAt(i % threads.length), "<context_eval>")); } for (int i = 0; i < threads.length; i++) { threads[i].start(); @@ -122,12 +130,14 @@ public class FastRContext { } } else { for (int i = 0; i < contexts.getLength(); i++) { - RContext context = checkContext(contexts.getDataAt(i), this); + ContextInfo info = checkContext(contexts.getDataAt(i), this); + TruffleVM vm = info.newContext(); try { - context.activate(); - context.getThisEngine().parseAndEval(Source.fromText(exprs.getDataAt(i), "<context_eval>"), true, false); + vm.eval("application/x-r", exprs.getDataAt(i)); + } catch (IOException e) { + throw RInternalError.shouldNotReachHere(e); } finally { - context.destroy(); + RContext.destroyContext(vm); } } } @@ -135,12 +145,12 @@ public class FastRContext { } } - private static RContext checkContext(int contextId, RBaseNode invokingNode) throws RError { - RContext context = RContext.find(contextId); - if (context == null) { + private static ContextInfo checkContext(int contextId, RBaseNode invokingNode) throws RError { + ContextInfo info = ContextInfo.get(contextId); + if (info == null) { throw RError.error(invokingNode, RError.Message.GENERIC, "no context: " + contextId); } else { - return context; + return info; } } @@ -155,6 +165,7 @@ public class FastRContext { public abstract static class CreateChannel extends RExternalBuiltinNode.Arg1 { @Specialization + @TruffleBoundary protected int createChannel(RAbstractVector key) { validateChannelArg(this, key); return RChannel.createChannel(((RAbstractIntVector) key).getDataAt(0)); @@ -163,6 +174,7 @@ public class FastRContext { public abstract static class GetChannel extends RExternalBuiltinNode.Arg1 { @Specialization + @TruffleBoundary protected int getChannel(RAbstractVector key) { validateChannelArg(this, key); return RChannel.getChannel(((RAbstractIntVector) key).getDataAt(0)); @@ -171,6 +183,7 @@ public class FastRContext { public abstract static class CloseChannel extends RExternalBuiltinNode.Arg1 { @Specialization + @TruffleBoundary protected RNull getChannel(RAbstractVector id) { validateChannelArg(this, id); RChannel.closeChannel(((RAbstractIntVector) id).getDataAt(0)); @@ -180,6 +193,7 @@ public class FastRContext { public abstract static class ChannelSend extends RExternalBuiltinNode.Arg2 { @Specialization + @TruffleBoundary protected RNull send(RAbstractVector id, Object data) { validateChannelArg(this, id); RChannel.send(((RAbstractIntVector) id).getDataAt(0), data); @@ -189,6 +203,7 @@ public class FastRContext { public abstract static class ChannelReceive extends RExternalBuiltinNode.Arg1 { @Specialization + @TruffleBoundary protected Object receive(RAbstractVector id) { validateChannelArg(this, id); return RChannel.receive(((RAbstractIntVector) id).getDataAt(0)); diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRStackTrace.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRStackTrace.java index c545129a94f5fbfe4c0a80ca395e18fdf09ba63c..d477d07a42850890f62fee67563e99852ddf7264 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRStackTrace.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/FastRStackTrace.java @@ -25,6 +25,7 @@ package com.oracle.truffle.r.library.fastr; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; public abstract class FastRStackTrace extends RExternalBuiltinNode.Arg1 { diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropExport.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropExport.java new file mode 100644 index 0000000000000000000000000000000000000000..b540f61ccfb335df594d93d3892050ca8ad0b5d9 --- /dev/null +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropExport.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.library.fastr; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.data.*; + +public abstract class InteropExport extends RExternalBuiltinNode.Arg2 { + + @Specialization + @TruffleBoundary + protected Object debugSource(Object name, RTypedValue value) { + String stringName = RRuntime.asString(name); + if (stringName == null) { + throw RError.error(this, RError.Message.INVALID_ARG_TYPE, "name"); + } + if (value instanceof TruffleObject) { + RContext.getInstance().getExportedSymbols().put(stringName, (TruffleObject) value); + } else { + throw RError.error(this, RError.Message.NO_INTEROP, "value", value.getClass().getSimpleName()); + } + return RNull.instance; + } +} diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropImport.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropImport.java new file mode 100644 index 0000000000000000000000000000000000000000..59781981ac924495c2fa586c76c7114f53279698 --- /dev/null +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastr/InteropImport.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.library.fastr; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; + +public abstract class InteropImport extends RExternalBuiltinNode.Arg1 { + + @Specialization + @TruffleBoundary + protected Object debugSource(Object name) { + String stringName = RRuntime.asString(name); + if (stringName == null) { + throw RError.error(this, RError.Message.INVALID_ARG_TYPE, "name"); + } + Object object = RContext.getInstance().getEnv().importSymbol(stringName); + if (object == null) { + throw RError.error(this, RError.Message.NO_IMPORT_OBJECT, stringName); + } + return object; + } +} diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java index 72ec09640886fd7330ecc5ee0a1e56b9e48c0627..9bf7e1daa608cdcb6fcf3c8985c66d0aa1a791c5 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java @@ -15,8 +15,7 @@ package com.oracle.truffle.r.library.graphics; import com.oracle.truffle.r.library.graphics.core.*; -import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java index 2cd94ebed16774ba854bd346aa4259c773c98303..ec801c5738807ea575b3516daf8418ec46b04bb5 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java @@ -11,10 +11,11 @@ */ package com.oracle.truffle.r.library.methods; -import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java index 3128645a2b09504dbdc3dda59c488ad89b22cdc5..3ab8aedea9a9d78243d48122d9bf2d59f03faab0 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java @@ -15,7 +15,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.ConsoleHandler; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.model.*; // Translated from GnuR: library/utils/io.c diff --git a/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE b/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE index ab0090b5af4eab4bbc16cd707eb132c49d9c865c..18ff461a6906f32a5728bb96e1c6c193c9761ddc 100644 --- a/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE +++ b/com.oracle.truffle.r.native/library/fastr/src/NAMESPACE @@ -1,4 +1,6 @@ ## exported functions +export(Interop.import) +export(Interop.export) export(fastr.createcc) export(fastr.getcc) export(fastr.compile) diff --git a/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R b/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R index d782c83b104bdb686dec7a1dffce65a3f161c9c7..93883096c09f91c26293da88490853b09d58038b 100644 --- a/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R +++ b/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R @@ -21,6 +21,10 @@ # questions. # +Interop.import <- function(name) invisible(.FastR(.NAME="Interop.import", name)) + +Interop.export <- function(name, value) invisible(.FastR(.NAME="Interop.export", name, value)) + fastr.createcc <- function(func) invisible(.FastR(.NAME="createcc", func)) fastr.getcc <- function(func) .FastR(.NAME="getcc", func) diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java index 70c5d407d1a25036ade83ece7b88aa68eebcbf33..27b3079b8e1c23aae48502853e1b2d6bd805cd12 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java @@ -29,6 +29,8 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.nodes.builtin.RBuiltinFactory.NodeGenFactory; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; import com.oracle.truffle.r.runtime.env.*; /** @@ -139,7 +141,11 @@ public abstract class RBuiltinPackage { ArrayList<Source> sources = rSources.get(getName()); if (sources != null) { for (Source source : sources) { - RContext.getEngine().parseAndEval(source, frame, false, false); + try { + RContext.getEngine().parseAndEval(source, frame, false); + } catch (ParseException e) { + throw new RInternalError(e, "error while parsing overrides from %s", source.getName()); + } } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java index 34ba07e67d34cd4a1665e7a168b66b3efb8dc92e..1d10d77bf0acacb9660a2ec677274e4f59107011 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java @@ -31,6 +31,8 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.nodes.builtin.base.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; @@ -95,7 +97,11 @@ public final class RBuiltinPackages implements RBuiltinLookup { // Any RBuiltinKind.SUBSTITUTE functions installed above should not be overridden try { RContext.getInstance().setLoadingBase(true); - RContext.getEngine().parseAndEval(baseSource, frame, false, false); + try { + RContext.getEngine().parseAndEval(baseSource, frame, false); + } catch (ParseException e) { + throw new RInternalError(e, "error while parsing base source from %s", baseSource.getName()); + } } finally { RContext.getInstance().setLoadingBase(false); } @@ -103,7 +109,7 @@ public final class RBuiltinPackages implements RBuiltinLookup { } public static void loadDefaultPackageOverrides() { - Object defaultPackages = RContext.getROptionsState().getValue("defaultPackages"); + Object defaultPackages = RContext.getInstance().stateROptions.getValue("defaultPackages"); if (defaultPackages instanceof RAbstractStringVector) { RAbstractStringVector defPkgs = (RAbstractStringVector) defaultPackages; for (int i = 0; i < defPkgs.getLength(); i++) { @@ -118,16 +124,25 @@ public final class RBuiltinPackages implements RBuiltinLookup { */ REnvironment env = REnvironment.baseEnv(); for (Source source : componentList) { - RContext.getEngine().parseAndEval(source, env.getFrame(), false, false); + try { + RContext.getEngine().parseAndEval(source, env.getFrame(), false); + } catch (ParseException e) { + throw new RInternalError(e, "error while parsing default package override from %s", source.getName()); + } } } } } + /** + * Global builtin cache. + */ + private static final HashMap<Object, RFunction> cachedBuiltinFunctions = new HashMap<>(); + @Override public RFunction lookupBuiltin(String methodName) { CompilerAsserts.neverPartOfCompilation(); - RFunction function = RContext.getCachedBuiltin(methodName); + RFunction function = cachedBuiltinFunctions.get(methodName); if (function != null) { return function; } @@ -142,7 +157,9 @@ public final class RBuiltinPackages implements RBuiltinLookup { private static RFunction createFunction(RBuiltinFactory builtinFactory, String methodName) { try { RootCallTarget callTarget = RBuiltinNode.createArgumentsCallTarget(builtinFactory); - return RContext.cacheBuiltin(methodName, RDataFactory.createFunction(builtinFactory.getName(), callTarget, builtinFactory, REnvironment.baseEnv().getFrame(), false)); + RFunction function = RDataFactory.createFunction(builtinFactory.getName(), callTarget, builtinFactory, REnvironment.baseEnv().getFrame(), false); + cachedBuiltinFunctions.put(methodName, function); + return function; } catch (Throwable t) { throw new RuntimeException("error while creating builtin " + methodName + " / " + builtinFactory, t); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java index 50c30abeee51ac9bf069b35c6406f61204a15a05..211b97031e21e51d3b255b8439c547780526ff59 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java @@ -31,6 +31,7 @@ import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.instrument.debug.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java index 5b1d9b3d0b31d9b2f78e858d887f3fed7cd1f4c8..f15aa93ed9df348990acfb6482acd06bf0e05c3b 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java @@ -34,6 +34,7 @@ import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; @@ -87,7 +88,7 @@ public abstract class Cat extends RInvisibleBuiltinNode { checkFillLength(fill); int fillWidth = -1; if (RRuntime.fromLogical(fill.getDataAt(0))) { - fillWidth = ((RIntVector) RContext.getROptionsState().getValue("width")).getDataAt(0); + fillWidth = ((RIntVector) RContext.getInstance().stateROptions.getValue("width")).getDataAt(0); } return output(args, conn, sepVec, fillWidth, checkLabels(labels), append); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java index d09b54b706805e0e708c7650568ca2161360cd22..f51aec4d57980e80f26828a19bfb981af9bfd456 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; @RBuiltin(name = "commandArgs", kind = INTERNAL, parameterNames = {}) @@ -41,7 +42,7 @@ public abstract class CommandArgs extends RBuiltinNode { @TruffleBoundary private static RStringVector getCommandArgs() { - String[] s = RContext.getInstance().getCommandArgs(); + String[] s = RContext.getInstance().getOptions().getArguments(); return RDataFactory.createStringVector(s, RDataFactory.COMPLETE_VECTOR); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java index d2c75c5d3e1e9a9d338477f485203e4b750070d1..d8df9077ee2d91490f397bccfd3655049b256c24 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java @@ -44,6 +44,7 @@ import com.oracle.truffle.r.runtime.conn.*; import com.oracle.truffle.r.runtime.conn.SocketConnections.RSocketConnection; import com.oracle.truffle.r.runtime.conn.TextConnections.TextRConnection; import com.oracle.truffle.r.runtime.conn.URLConnections.URLRConnection; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; @@ -266,9 +267,9 @@ public abstract class ConnectionFunctions { protected RList summary(Object object) { BaseRConnection baseCon; if (object instanceof Integer) { - baseCon = RContext.getRConnectionState().getConnection((int) object); + baseCon = RContext.getInstance().stateRConnection.getConnection((int) object); } else if (object instanceof Double) { - baseCon = RContext.getRConnectionState().getConnection((int) Math.floor((Double) object)); + baseCon = RContext.getInstance().stateRConnection.getConnection((int) Math.floor((Double) object)); } else { RConnection con = checkIsConnection(object); baseCon = getBaseConnection(con); @@ -943,7 +944,7 @@ public abstract class ConnectionFunctions { @TruffleBoundary protected RConnection getConnection(int what) { controlVisibility(); - BaseRConnection con = RContext.getRConnectionState().getConnection(what); + BaseRConnection con = RContext.getInstance().stateRConnection.getConnection(what); if (con == null) { throw RError.error(this, RError.Message.NO_SUCH_CONNECTION, what); } else { @@ -958,7 +959,7 @@ public abstract class ConnectionFunctions { @TruffleBoundary protected RIntVector getAllConnections() { controlVisibility(); - return RContext.getRConnectionState().getAllConnections(); + return RContext.getInstance().stateRConnection.getAllConnections(); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java index a2ac1cedaf928417c28c02647380871588a32e44..cb86d84657c0b46579888d9616e8d09bbf891d8d 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java @@ -21,6 +21,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java index c33c9cb1d4000e17aeb62159f14b7cda1ac47f9d..0b6c8bd9a8762b2b291574dd9538e42277f9e466 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java @@ -191,5 +191,11 @@ public class DuplicatedFunctions { protected int anyDuplicatedEmpty(RAbstractContainer x, RAbstractContainer incomparables, byte fromLast) { return 0; } + + @SuppressWarnings("unused") + @Specialization + protected int anyDuplicatedNull(RNull x, RAbstractContainer incomparables, byte fromLast) { + return 0; + } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java index 8d701b7251b224c6acfd15f5a3f32d847674053b..790ff3db820762373764493107fab58f06337d16 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java @@ -24,16 +24,16 @@ package com.oracle.truffle.r.nodes.builtin.base; import static com.oracle.truffle.r.runtime.RBuiltinKind.*; -import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.r.nodes.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; -import com.oracle.truffle.r.runtime.nodes.*; import com.oracle.truffle.r.runtime.env.*; -import com.oracle.truffle.r.runtime.env.REnvironment.*; +import com.oracle.truffle.r.runtime.nodes.*; /** * The {@code eval} {@code .Internal} and the {@code withVisible} {@code .Primitive}. @@ -46,18 +46,10 @@ public class EvalFunctions { protected Object doEvalBody(int depth, Object exprArg, REnvironment envir, REnvironment enclos) { Object expr = RASTUtils.checkForRSymbol(exprArg); - if (RASTUtils.isLanguageOrExpression(expr)) { - try { - Object result; - if (expr instanceof RExpression) { - result = RContext.getEngine().eval((RExpression) expr, envir, enclos, depth); - } else { - result = RContext.getEngine().eval((RLanguage) expr, envir, enclos, depth); - } - return result; - } catch (PutException ex) { - throw RError.error(this, ex); - } + if (expr instanceof RExpression) { + return RContext.getEngine().eval((RExpression) expr, envir, enclos, depth); + } else if (expr instanceof RLanguage) { + return RContext.getEngine().eval((RLanguage) expr, envir, enclos, depth); } else { // just return value return expr; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java index 9e8604f557dd4a04d46ec2310d1d1b6617cb33fa..2282d892211616466be0632e643360ca2f34c0f3 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java @@ -20,6 +20,7 @@ import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java index d546d2e65d6dda34810b8faa7f1276c98423da3c..d3461965d2a0cb1ea5adceceb28af77a60f0ccbe 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java @@ -40,8 +40,9 @@ import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseDeoptimizeFr import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgNode; import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgsPromiseNode; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; -import com.oracle.truffle.r.runtime.data.RPromise.*; +import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.gnur.*; import com.oracle.truffle.r.runtime.nodes.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java index 6ad991cd42e2fa12fd9cafab5efa0951d5d25186..d34e75930e6fa893264168cddc0b7f1ad2a02a73 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java @@ -30,10 +30,11 @@ import com.oracle.truffle.r.nodes.runtime.*; import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; -import com.oracle.truffle.r.runtime.env.REnvironment.*; +import com.oracle.truffle.r.runtime.env.REnvironment.PutException; import com.oracle.truffle.r.runtime.ffi.*; import com.oracle.truffle.r.runtime.nodes.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java index 739ba519a5fb4ec15221491340903ad42451f44d..4e19b00adffb2b361dc4f17b7e35f2468fff359c 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java @@ -35,6 +35,7 @@ import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.access.*; import com.oracle.truffle.r.nodes.access.array.read.*; import com.oracle.truffle.r.nodes.access.array.write.*; +import com.oracle.truffle.r.nodes.access.vector.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctionsFactory.PromiseEvaluatorNodeGen; import com.oracle.truffle.r.nodes.function.*; @@ -178,8 +179,11 @@ public class InfixEmulationFunctions { public abstract static class AccessArrayBuiltin extends RBuiltinNode { + // old @Child private AccessArrayNode accessNode; @Child private AccessPositions positions; + // new + @Child private ExtractVectorNode extractNode; @Override protected void createCasts(CastBuilder casts) { @@ -189,16 +193,27 @@ public class InfixEmulationFunctions { protected abstract boolean isSubset(); protected Object access(Object vector, byte exact, RArgsValuesAndNames inds, Object dropDim) { - if (accessNode == null || positions.getLength() != inds.getLength()) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - if (accessNode == null) { - accessNode = insert(AccessArrayNodeGen.create(isSubset(), false, false, null, null, null, null, null)); + Object result; + if (ExtractVectorNode.USE_NODE) { + if (extractNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + extractNode = insert(ExtractVectorNode.create(isSubset() ? ElementAccessMode.SUBSET : ElementAccessMode.SUBSCRIPT)); } - int len = inds.getLength(); - positions = insert(AccessPositions.create(isSubset(), len)); + result = extractNode.apply(vector, inds.getArguments(), RLogical.valueOf(exact), dropDim); + } else { + if (accessNode == null || positions.getLength() != inds.getLength()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + if (accessNode == null) { + accessNode = insert(AccessArrayNodeGen.create(isSubset(), false, false, null, null, null, null, null)); + } + int len = inds.getLength(); + positions = insert(AccessPositions.create(isSubset(), len)); + } + Object[] pos = inds.getArguments(); + result = accessNode.executeAccess(vector, exact, 0, positions.execute(vector, pos, exact, pos), dropDim); } - Object[] pos = inds.getArguments(); - return accessNode.executeAccess(vector, exact, 0, positions.execute(vector, pos, exact, pos), dropDim); + return result; + } protected boolean noInd(RArgsValuesAndNames inds) { @@ -273,6 +288,10 @@ public class InfixEmulationFunctions { } protected Object getInternal(Object x, RArgsValuesAndNames inds, RAbstractLogicalVector exactVec) { + /* + * TODO this should not be handled here. The new vector access nodes handle this, remove + * this check as soon as its the default and the old implementation is gone. + */ byte exact; if (emptyExactProfile.profile(exactVec.getLength() == 0)) { exact = RRuntime.LOGICAL_FALSE; @@ -323,6 +342,7 @@ public class InfixEmulationFunctions { public abstract static class UpdateArrayBuiltin extends RBuiltinNode { @Child private UpdateArrayHelperNode updateNode; + @Child private ReplaceVectorNode replaceNode; @Child private UpdatePositions positions; @Child private CoerceVector coerceVector; @@ -330,24 +350,40 @@ public class InfixEmulationFunctions { private final ConditionProfile argsLengthLargerThanOneProfile = ConditionProfile.createBinaryProfile(); protected Object update(Object vector, RArgsValuesAndNames args, Object value, boolean isSubset) { - int len = argsLengthOneProfile.profile(args.getLength() == 1) ? 1 : args.getLength() - 1; - - if (updateNode == null || positions.getLength() != len) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - if (updateNode == null) { - updateNode = insert(UpdateArrayHelperNodeGen.create(isSubset, 0, null, null, null, null)); + Object result; + if (ReplaceVectorNode.USE_NODE) { + if (replaceNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + replaceNode = insert(ReplaceVectorNode.create(isSubset ? ElementAccessMode.SUBSET : ElementAccessMode.SUBSCRIPT)); } - positions = insert(UpdatePositions.create(isSubset, len)); - coerceVector = insert(CoerceVectorNodeGen.create(null, null, null)); - } - Object[] pos; - if (argsLengthLargerThanOneProfile.profile(args.getLength() > 1)) { - pos = Arrays.copyOf(args.getArguments(), args.getLength() - 1); + Object[] pos; + if (argsLengthLargerThanOneProfile.profile(args.getLength() > 1)) { + pos = Arrays.copyOf(args.getArguments(), args.getLength() - 1); + } else { + pos = new Object[]{RMissing.instance}; + } + result = replaceNode.apply(vector, pos, value); } else { - pos = new Object[]{RMissing.instance}; + int len = argsLengthOneProfile.profile(args.getLength() == 1) ? 1 : args.getLength() - 1; + + if (updateNode == null || positions.getLength() != len) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + if (updateNode == null) { + updateNode = insert(UpdateArrayHelperNodeGen.create(isSubset, 0, null, null, null, null)); + } + positions = insert(UpdatePositions.create(isSubset, len)); + coerceVector = insert(CoerceVectorNodeGen.create(null, null, null)); + } + Object[] pos; + if (argsLengthLargerThanOneProfile.profile(args.getLength() > 1)) { + pos = Arrays.copyOf(args.getArguments(), args.getLength() - 1); + } else { + pos = new Object[]{RMissing.instance}; + } + Object newPositions = positions.execute(vector, pos, pos, value); + result = updateNode.executeUpdate(vector, value, newPositions, coerceVector.executeEvaluated(value, vector, newPositions)); } - Object newPositions = positions.execute(vector, pos, pos, value); - return updateNode.executeUpdate(vector, value, newPositions, coerceVector.executeEvaluated(value, vector, newPositions)); + return result; } @SuppressWarnings("unused") diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java index e5bf51358f6286c9687d6c78f8dee45456f6ce3f..9143345171db8b1edf46550b1ff96fadcc257f4a 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java @@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; @RBuiltin(name = "interactive", kind = RBuiltinKind.PRIMITIVE, parameterNames = {}) public abstract class Interactive extends RBuiltinNode { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java index b3693a4b7ebd0e3da038a1bea9df16c1363c1c76..067a477758c8e611350be46a73de15485e51c804 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java @@ -36,6 +36,7 @@ import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.nodes.function.RCallNode.LeafCallNode; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.nodes.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java index f161f499c631ebdf56a91f4440119fc78727983b..19e003c76728be88674da6ce224893a0272908de 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java @@ -26,7 +26,8 @@ import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.builtin.base.LapplyNodeGen.LapplyInternalNodeGen; import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.Engine.ParseException; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java index 1415094a7a2e28ad3676868b097a7338c9d4c5c2..979f842286b719c8a2b58fc557d50fcdac577c9e 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java @@ -347,6 +347,18 @@ public abstract class Match extends RBuiltinNode { return match(RClosures.createLogicalToStringVector(x), table, nomatchObj, incomparables); } + @Specialization + protected RIntVector match(RAbstractIntVector x, RAbstractStringVector table, Object nomatchObj, Object incomparables) { + naCheck.enable(x); + return match(RClosures.createIntToStringVector(x), table, nomatchObj, incomparables); + } + + @Specialization + protected RIntVector match(RAbstractDoubleVector x, RAbstractStringVector table, Object nomatchObj, Object incomparables) { + naCheck.enable(x); + return match(RClosures.createDoubleToStringVector(x), table, nomatchObj, incomparables); + } + @Specialization @SuppressWarnings("unused") protected RIntVector match(RAbstractLogicalVector x, RAbstractLogicalVector table, Object nomatchObj, Object incomparables) { diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Options.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Options.java index 680856255c188993e4cb439465dd5eeb6bad95b7..3624eb87eb15013a170e06bd9e3e124d4214cfe7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Options.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Options.java @@ -33,6 +33,7 @@ import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.ROptions.OptionsException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; @RBuiltin(name = "options", kind = INTERNAL, parameterNames = {"..."}) @@ -48,7 +49,7 @@ public abstract class Options extends RBuiltinNode { @Specialization protected RList options(@SuppressWarnings("unused") RMissing x) { controlVisibility(); - Set<Map.Entry<String, Object>> optionSettings = RContext.getROptionsState().getValues(); + Set<Map.Entry<String, Object>> optionSettings = RContext.getInstance().stateROptions.getValues(); Object[] data = new Object[optionSettings.size()]; String[] names = new String[data.length]; int i = 0; @@ -69,7 +70,7 @@ public abstract class Options extends RBuiltinNode { @TruffleBoundary protected Object options(RArgsValuesAndNames args) { try { - ROptions.ContextState options = RContext.getROptionsState(); + ROptions.ContextStateImpl options = RContext.getInstance().stateROptions; Object[] values = args.getArguments(); ArgumentsSignature signature = args.getSignature(); Object[] data = new Object[values.length]; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java index adb5042c175fba3af175ff74896e36820ebf0f3c..f465111a0e5e9fa53ab902e4b914b5fd2aacb86d 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java @@ -33,8 +33,9 @@ import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.Engine.ParseException; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java index 0185ebe7fce584f0b070558e84fd215eaacc8b22..b746c83368714956b52197f2166640f39a9997e3 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java @@ -44,6 +44,7 @@ import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute; import com.oracle.truffle.r.runtime.data.closures.*; @@ -425,7 +426,7 @@ public abstract class PrettyPrinterNode extends RNode { private static int getMaxPrintLength() { int maxPrint = -1; // infinity - Object maxPrintObj = RContext.getROptionsState().getValue("max.print"); + Object maxPrintObj = RContext.getInstance().stateROptions.getValue("max.print"); if (maxPrintObj != null) { if (maxPrintObj instanceof Integer) { @@ -611,7 +612,7 @@ public abstract class PrettyPrinterNode extends RNode { if (rowHeaderUsesIndices(dimNames)) { return concat("[", intString(c), ",]"); } else { - RStringVector dimNamesVector = (RStringVector) dimNames.getDataAt(0); + RAbstractStringVector dimNamesVector = (RAbstractStringVector) getDimNamesAt(dimNames, 1); String dimId = dimNamesVector.getDataAt(c - 1); if (RRuntime.isNA(dimId)) { dimId = RRuntime.NA_HEADER; @@ -630,15 +631,23 @@ public abstract class PrettyPrinterNode extends RNode { private static String getDimId(RAbstractVector vector, int dimLevel, int dimInd, RAttributeProfiles attrProfiles) { String dimId; RList dimNames = vector.getDimNames(attrProfiles); - if (dimNames == null || dimNames.getDataAt(dimLevel - 1) == RNull.instance) { + if (dimNames == null || getDimNamesAt(dimNames, dimLevel) == RNull.instance) { dimId = intString(dimInd + 1); } else { - RStringVector dimNamesVector = (RStringVector) dimNames.getDataAt(dimLevel - 1); + RAbstractStringVector dimNamesVector = (RAbstractStringVector) getDimNamesAt(dimNames, dimLevel); dimId = dimNamesVector.getDataAt(dimInd); } return dimId; } + private static Object getDimNamesAt(RList dimNames, int dimLevel) { + Object result = dimNames.getDataAt(dimLevel - 1); + if (result instanceof String) { + return RString.valueOf((String) result); + } + return result; + } + private static double calcRoundFactor(double input, long maxFactor) { if (Double.isNaN(input) || Double.isInfinite(input) || input == 0.0) { return maxFactor * 10; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java index d7f3c2736ac618bac2a2817535d822f1623557a3..2fc55db2512244cc29850d1b03baf58b99372d45 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java @@ -28,6 +28,7 @@ import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; @RBuiltin(name = ".Primitive", kind = PRIMITIVE, parameterNames = "name") diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java index b0f6c5fa3948fc1c1a394bd2aaedef14db90b37f..d24720bda8ebbdd78ed043a599ea552ffad53afc 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java @@ -30,6 +30,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; @RBuiltin(name = "proc.time", kind = PRIMITIVE, parameterNames = {}) diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java index df8889cdda6dcc2c43d3f54b8a70fa4f207eeded..7abe3ad2dd4747a75d5ef950db951f0fa49753e4 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java @@ -28,6 +28,8 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; @RBuiltin(name = "quit", kind = INTERNAL, parameterNames = {"save", "status", "runLast"}) @@ -40,13 +42,13 @@ public abstract class Quit extends RInvisibleBuiltinNode { @Specialization @TruffleBoundary - protected Object doQuit(final String saveArg, int status, byte runLast) { + protected Object doQuit(String saveArg, int status, byte runLast) { controlVisibility(); String save = saveArg; // Quit does not divert its output to sink - RContext.ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler(); + ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler(); if (save.equals("default")) { - if (RCmdOptions.NO_SAVE.getValue()) { + if (RContext.getInstance().getOptions().getBoolean(RCmdOption.NO_SAVE)) { save = "no"; } else { if (consoleHandler.isInteractive()) { @@ -92,10 +94,8 @@ public abstract class Quit extends RInvisibleBuiltinNode { // TODO errors should return to prompt if interactive RContext.getEngine().checkAndRunLast(".Last.sys"); } - // destroy the context - RContext.getInstance().destroy(); + // destroy the context inside exit() method as it still needs to access it Utils.exit(status); return null; } - } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java index 09ff05dc08d2501a6b69c1eddfbe320ad9698868..da577263fd61331d03439d52ab327038d98bb8ef 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java @@ -30,6 +30,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.model.*; @RBuiltin(name = "readRenviron", kind = INTERNAL, parameterNames = "path") @@ -42,7 +43,7 @@ public abstract class ReadREnviron extends RInvisibleBuiltinNode { String path = Utils.tildeExpand(vec.getDataAt(0)); byte result = RRuntime.LOGICAL_TRUE; try { - REnvVars.readEnvironFile(path); + RContext.getInstance().stateREnvVars.readEnvironFile(path); } catch (FileNotFoundException ex) { RError.warning(this, RError.Message.GENERIC, ex.getMessage()); result = RRuntime.LOGICAL_FALSE; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java index 434b265a8ae08dbc39b23537a9b69bb0cd057152..90e161c0f66dc1025a50778676acea45e023b52e 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java @@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.model.*; @RBuiltin(name = "readline", kind = RBuiltinKind.INTERNAL, parameterNames = "prompt") @@ -34,7 +35,7 @@ public abstract class Readline extends RBuiltinNode { if (!RContext.getInstance().isInteractive()) { return ""; } - RContext.ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler(); + ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler(); String savedPrompt = consoleHandler.getPrompt(); consoleHandler.setPrompt(prompt.getDataAt(0)); String input = consoleHandler.readLine(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java index d2ce8f5a27da612593793c34d02e5922c8eb96e2..33de4d8aafc3482e7dd4c929f4f4a2df1f2bf0b2 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java @@ -22,21 +22,22 @@ */ package com.oracle.truffle.r.nodes.builtin.base; -import static com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import static com.oracle.truffle.r.runtime.RBuiltinKind.*; import java.io.*; import java.util.*; import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; -import com.oracle.truffle.api.utilities.ConditionProfile; +import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; -import com.oracle.truffle.r.runtime.ffi.*; import com.oracle.truffle.r.runtime.ffi.BaseRFFI.UtsName; +import com.oracle.truffle.r.runtime.ffi.*; public class SysFunctions { @@ -60,7 +61,7 @@ public class SysFunctions { @TruffleBoundary protected Object sysGetEnv(RAbstractStringVector x, RAbstractStringVector unset) { controlVisibility(); - Map<String, String> envMap = REnvVars.getMap(); + Map<String, String> envMap = RContext.getInstance().stateREnvVars.getMap(); int len = x.getLength(); if (zeroLengthProfile.profile(len == 0)) { String[] data = new String[envMap.size()]; @@ -102,12 +103,13 @@ public class SysFunctions { @RBuiltin(name = "Sys.setenv", kind = INTERNAL, parameterNames = {"nm", "values"}) public abstract static class SysSetEnv extends RInvisibleBuiltinNode { - @Specialization() + @Specialization @TruffleBoundary protected RLogicalVector doSysSetEnv(RStringVector names, RStringVector values) { byte[] data = new byte[names.getLength()]; + REnvVars stateREnvVars = RContext.getInstance().stateREnvVars; for (int i = 0; i < data.length; i++) { - REnvVars.put(names.getDataAt(i), values.getDataAt(i)); + stateREnvVars.put(names.getDataAt(i), values.getDataAt(i)); data[i] = RRuntime.LOGICAL_TRUE; } return RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR); @@ -122,8 +124,9 @@ public class SysFunctions { @TruffleBoundary protected RLogicalVector doSysSetEnv(RAbstractStringVector argVec) { byte[] data = new byte[argVec.getLength()]; + REnvVars stateREnvVars = RContext.getInstance().stateREnvVars; for (int i = 0; i < data.length; i++) { - data[i] = RRuntime.asLogical(REnvVars.unset(argVec.getDataAt(i))); + data[i] = RRuntime.asLogical(stateREnvVars.unset(argVec.getDataAt(i))); } return RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java index c91767c25b35411edf59b3f03c29c7c099b7e140..e050f36c17bb9bb953f4d5e0026b7f5a8ae63e87 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java @@ -23,13 +23,14 @@ package com.oracle.truffle.r.nodes.builtin.base; import java.io.*; -import java.lang.ProcessBuilder.*; +import java.lang.ProcessBuilder.Redirect; import java.util.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; @@ -40,7 +41,7 @@ public abstract class SystemFunction extends RBuiltinNode { public Object system(RAbstractStringVector command, byte internLogical) { Object result; boolean intern = RRuntime.fromLogical(internLogical); - String shell = REnvVars.get("SHELL"); + String shell = RContext.getInstance().stateREnvVars.get("SHELL"); if (shell == null) { shell = "/bin/sh"; } @@ -93,7 +94,7 @@ public abstract class SystemFunction extends RBuiltinNode { private static void updateEnvironment(ProcessBuilder pb) { Map<String, String> pEnv = pb.environment(); - Map<String, String> rEnv = REnvVars.getMap(); + Map<String, String> rEnv = RContext.getInstance().stateREnvVars.getMap(); for (Map.Entry<String, String> entry : rEnv.entrySet()) { String name = entry.getKey(); String value = entry.getValue(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java index 4c67af3936af368f14534f1adfed31c748b0e2ce..632d9c5ed2405a84b3daf2b2f5389656efe01d09 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java @@ -29,8 +29,9 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.r.nodes.builtin.*; -import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.*; +import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.nodes.*; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java index c8f9b77579392aa2d219c2afc57a6cad0b905116..271ecc6d1afb84cc8d4b3a4ecc75ba34cbcc7096 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FastR.java @@ -56,6 +56,10 @@ public abstract class FastR extends RBuiltinNode { protected RExternalBuiltinNode lookupName(RAbstractStringVector name) { switch (name.getDataAt(0)) { + case "Interop.import": + return InteropImportNodeGen.create(); + case "Interop.export": + return InteropExportNodeGen.create(); case "createcc": return FastRCallCountingFactory.CreateCallCounterNodeGen.create(); case "getcc": @@ -102,7 +106,6 @@ public abstract class FastR extends RBuiltinNode { return FastRContextFactory.ChannelSendNodeGen.create(); case "fastr.channel.receive": return FastRContextFactory.ChannelReceiveNodeGen.create(); - default: return null; } diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNodeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8f35da8a79f442cebb840fca4b81a81afd40c092 --- /dev/null +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNodeTest.java @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import static com.oracle.truffle.r.nodes.test.TestUtilities.*; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.*; +import static org.junit.Assume.*; + +import org.junit.*; +import org.junit.experimental.theories.*; +import org.junit.runner.*; + +import com.oracle.truffle.r.nodes.binary.*; +import com.oracle.truffle.r.nodes.test.*; +import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +@RunWith(Theories.class) +public class ExtractVectorNodeTest extends TestBase { + + @DataPoints public static RType[] vectorTypes = RType.getVectorTypes(); + + @DataPoint public static RAbstractVector accessEmpty = RDataFactory.createEmptyLogicalVector(); + @DataPoint public static RAbstractVector accessFirst = RDataFactory.createIntVector(new int[]{1}, true); + @DataPoint public static RAbstractVector accessSecond = RDataFactory.createIntVector(new int[]{2}, true); + @DataPoint public static RAbstractVector accessEverythingButFirst = RDataFactory.createIntVector(new int[]{-1}, true); + @DataPoint public static RAbstractVector accessNA = RDataFactory.createIntVector(new int[]{RRuntime.INT_NA}, false); + @DataPoint public static RAbstractVector accessZero = RDataFactory.createIntVector(new int[]{0}, false); + @DataPoint public static RAbstractVector accessFirstTwo = RDataFactory.createIntVector(new int[]{1, 2}, true); + + @DataPoint public static RAbstractVector accessPositiveSequence = RDataFactory.createIntSequence(1, 1, 2); + @DataPoint public static RAbstractVector accessPositiveSequenceStride2 = RDataFactory.createIntSequence(1, 2, 2); + @DataPoint public static RAbstractVector accessNegativeSequence = RDataFactory.createIntSequence(-1, -1, 2); + + @DataPoints public static ElementAccessMode[] allModes = ElementAccessMode.values(); + + @Test + public void testSubsetMultiDimension() { + RAbstractIntVector vector; + + // replace rectangle with rectangle indices + vector = generateInteger(20, true); + vector.setDimensions(new int[]{5, 4}); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + RDataFactory.createIntVector(new int[]{2, 3, 4}, true), RDataFactory.createIntVector(new int[]{2, 3}, true)); + assertIndicies(vector, 6, 7, 8, 11, 12, 13); + + // replace box with box indices + vector = generateInteger(9, true); + vector.setDimensions(new int[]{3, 3}); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + RDataFactory.createIntVector(new int[]{2, 3}, true), RDataFactory.createIntVector(new int[]{2, 3}, true)); + assertIndicies(vector, 4, 5, 7, 8); + + // replace three dimensions + vector = generateInteger(24, true); + vector.setDimensions(new int[]{2, 3, 4}); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2}, true)); + assertIndicies(vector, 9); + + // replace three dimensions + vector = generateInteger(24, true); + vector.setDimensions(new int[]{2, 3, 4}); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2, 3}, true), RDataFactory.createIntVector(new int[]{2, 3, 4}, true)); + assertIndicies(vector, 9, 11, 15, 17, 21, 23); + + } + + private static void assertIndicies(RAbstractIntVector vector, int... expectedValues) { + assertThat(vector.getLength(), is(expectedValues.length)); + + int[] actual = new int[vector.getLength()]; + for (int i = 0; i < expectedValues.length; i++) { + actual[i] = vector.getDataAt(i); + } + assertThat(actual, is(expectedValues)); + } + + @Test + public void testSubsetSingleDimension() { + RAbstractIntVector vector; + + // extract scalar with logical vector with NA + vector = generateInteger(4, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_NA}, false)}); + assertIndicies(vector, 0, RRuntime.INT_NA, 2, RRuntime.INT_NA); + + // extract scalar with sequence stride=1 + vector = generateInteger(9, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntSequence(5, 1, 3)}); + assertIndicies(vector, 4, 5, 6); + + // extract scalar with sequence stride>1 + vector = generateInteger(9, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntSequence(5, 2, 2)}); + assertIndicies(vector, 4, 6); + + // extract scalar with negative integer vector + vector = generateInteger(4, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntVector(new int[]{-2}, true)}); + assertIndicies(vector, 0, 2, 3); + + // extract scalar with logical scalar + vector = generateInteger(3, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE}, true)}); + assertIndicies(vector, 0, 1, 2); + + // extract scalar with integer vector with NA + vector = generateInteger(4, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + new Object[]{RDataFactory.createIntVector(new int[]{1, RRuntime.INT_NA}, false)}); + assertIndicies(vector, 0, RRuntime.INT_NA); + + // extract scalar with logical vector + vector = generateInteger(4, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE}, true)}); + assertIndicies(vector, 0, 2); + + // extract vector indexed by logical vector + vector = generateInteger(4, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, // + new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE}, true)}); + assertIndicies(vector, 0, 2); + + // extract scalar with integer vector + vector = generateInteger(9, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntVector(new int[]{9, 8}, true)}); + assertIndicies(vector, 8, 7); + + // extract scalar with integer scalar + vector = generateInteger(9, true); + vector = executeExtract(ElementAccessMode.SUBSET, vector, new Object[]{RDataFactory.createIntVector(new int[]{9}, true)}); + assertIndicies(vector, 8); + + } + + @Theory + public void testNames(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, true); + + RStringVector names = (RStringVector) generateVector(RType.Character, 4, true); + vector.setNames(names); + RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RInteger.valueOf(2)); + + RStringVector newNames = result.getNames(RAttributeProfiles.create()); + assertThat(newNames.getLength(), is(1)); + assertThat(newNames.getDataAt(0), is(names.getDataAt(1))); + } + + @Theory + public void testOutOfBoundsAccess(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, true); + + RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RInteger.valueOf(5)); + + assertThat(vector.getRType(), is(result.getRType())); + assertThat(result.getLength(), is(1)); + Object expectedValue = targetType.create(1, true).getDataAtAsObject(0); + assertThat(result.getDataAtAsObject(0), is(expectedValue)); + } + + @Theory + public void testCompletenessOutOfBounds(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, true); + + assumeTrue(targetType != RType.Raw); + + RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RInteger.valueOf(10)); + + assertThat(result.isComplete(), is(false)); + } + + @Theory + public void testCompletenessAfterExtraction(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, false); + + assumeThat(vector.isComplete(), is(false)); + RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RInteger.valueOf(1)); + + assertThat(result.isComplete(), is(true)); + } + + @Theory + public void testCompletenessAfterSelectAll(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, false); + + assumeThat(vector.isComplete(), is(false)); + RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RLogical.valueOf(true)); + + assertThat(result.isComplete(), is(false)); + } + + @Theory + public void testCompletenessPositionNA(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, true); + + RAbstractVector result = executeExtract(ElementAccessMode.SUBSET, vector, RLogical.NA); + + assertThat(result.isComplete(), is(false)); + } + + @Theory + public void testSubsetSingleDimensionTheory(RType targetType, RAbstractVector position) { + assumeTrue(position.getLength() <= 4); + assumeTrue(position.getLength() >= 1); + + RAbstractVector vector = generateVector(targetType, 4, true); + + executeExtract(ElementAccessMode.SUBSET, vector, position); + } + + @Theory + public void testSubscriptSingleDimensionTheory(RType targetType, RAbstractVector position) { + assumeTrue(position.getLength() == 1); + if (position instanceof RAbstractIntVector) { + assumeTrue(((RAbstractIntVector) position).getDataAt(0) > 0); + } + + RAbstractVector vector = generateVector(targetType, 4, true); + + executeExtract(ElementAccessMode.SUBSCRIPT, vector, position); + } + + private NodeHandle<ExtractVectorNode> handle; + private NodeHandle<BoxPrimitiveNode> box; + private ElementAccessMode currentMode; + private boolean currentExact; + private boolean currentDropDimension; + + @Before + public void setUp() { + handle = null; + } + + @After + public void tearDown() { + handle = null; + } + + @SuppressWarnings("unchecked") + private <T extends RAbstractVector> T executeExtract(ElementAccessMode mode, T vector, Object... positions) { + return (T) executeExtract(mode, false, false, vector, positions); + } + + private RAbstractVector executeExtract(ElementAccessMode mode, boolean exact, boolean dropDimension, Object vector, Object... positions) { + if (handle == null || this.currentMode != mode || currentExact != exact || currentDropDimension != dropDimension) { + handle = create(mode, exact, dropDimension); + this.currentMode = mode; + } + if (box == null) { + box = createHandle(BoxPrimitiveNodeGen.create(), (node, args) -> node.execute(args[0])); + } + + return (RAbstractVector) box.call(handle.call(vector, positions)); + } + + private static NodeHandle<ExtractVectorNode> create(ElementAccessMode mode, boolean exact, boolean dropDimension) { + return createHandle(ExtractVectorNode.create(mode), // + (node, args) -> node.apply(args[0], (Object[]) args[1], RLogical.valueOf(exact), RLogical.valueOf(dropDimension))); + } + +} diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNodeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..65760088de1454f166846aa78b75c0b94ed65e0b --- /dev/null +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNodeTest.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import static com.oracle.truffle.r.nodes.test.TestUtilities.*; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.*; +import static org.junit.Assume.*; + +import org.junit.*; +import org.junit.experimental.theories.*; +import org.junit.runner.*; + +import com.oracle.truffle.r.nodes.binary.*; +import com.oracle.truffle.r.nodes.test.*; +import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +@RunWith(Theories.class) +public class ReplaceVectorNodeTest extends TestBase { + + @DataPoints public static RType[] vectorTypes = RType.getVectorTypes(); + + @DataPoint public static RAbstractVector accessEmpty = RDataFactory.createEmptyLogicalVector(); + @DataPoint public static RAbstractVector accessFirst = RDataFactory.createIntVector(new int[]{1}, true); + @DataPoint public static RAbstractVector accessSecond = RDataFactory.createIntVector(new int[]{2}, true); + @DataPoint public static RAbstractVector accessEverythingButFirst = RDataFactory.createIntVector(new int[]{-1}, true); + @DataPoint public static RAbstractVector accessNA = RDataFactory.createIntVector(new int[]{RRuntime.INT_NA}, false); + @DataPoint public static RAbstractVector accessZero = RDataFactory.createIntVector(new int[]{0}, false); + @DataPoint public static RAbstractVector accessFirstTwo = RDataFactory.createIntVector(new int[]{1, 2}, true); + + @DataPoint public static RAbstractVector accessPositiveSequence = RDataFactory.createIntSequence(1, 1, 2); + @DataPoint public static RAbstractVector accessPositiveSequenceStride2 = RDataFactory.createIntSequence(1, 2, 2); + @DataPoint public static RAbstractVector accessNegativeSequence = RDataFactory.createIntSequence(-1, -1, 2); + + @DataPoints public static ElementAccessMode[] allModes = ElementAccessMode.values(); + + @Test + public void testSubsetMultiDimension() { + RAbstractIntVector vector; + + // replace rectangle with rectangle indices + vector = generateInteger(20, true); + vector.setDimensions(new int[]{5, 4}); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), // + RDataFactory.createIntVector(new int[]{2, 3, 4}, true), RDataFactory.createIntVector(new int[]{2, 3}, true)); + assertIndicies(vector, 0, 1, 2, 3, 4, 5, -1, -1, -1, 9, 10, -1, -1, -1, 14, 15, 16, 17, 18, 19); + + // replace box with box indices + vector = generateInteger(9, true); + vector.setDimensions(new int[]{3, 3}); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), // + RDataFactory.createIntVector(new int[]{2, 3}, true), RDataFactory.createIntVector(new int[]{2, 3}, true)); + assertIndicies(vector, 0, 1, 2, 3, -1, -1, 6, -1, -1); + + // replace three dimensions + vector = generateInteger(24, true); + vector.setDimensions(new int[]{2, 3, 4}); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), // + RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2}, true)); + assertIndicies(vector, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23); + + // replace three dimensions + vector = generateInteger(24, true); + vector.setDimensions(new int[]{2, 3, 4}); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), // + RDataFactory.createIntVector(new int[]{2}, true), RDataFactory.createIntVector(new int[]{2, 3}, true), RDataFactory.createIntVector(new int[]{2, 3, 4}, true)); + assertIndicies(vector, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, 10, -1, 12, 13, 14, -1, 16, -1, 18, 19, 20, -1, 22, -1); + } + + private static void assertIndicies(RAbstractIntVector vector, int... expectedValues) { + int[] actual = new int[vector.getLength()]; + for (int i = 0; i < expectedValues.length; i++) { + actual[i] = vector.getDataAt(i); + + } + assertThat(actual, is(expectedValues)); + } + + @Test + public void testSubsetSingleDimension() { + RAbstractIntVector vector; + + // replace scalar with sequence stride=1 + vector = generateInteger(9, true); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntSequence(5, 1, 3)}); + assertIndicies(vector, 0, 1, 2, 3, -1, -1, -1, 7, 8); + + // replace scalar with sequence stride>1 + vector = generateInteger(9, true); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntSequence(5, 2, 2)}); + assertIndicies(vector, 0, 1, 2, 3, -1, 5, -1, 7, 8); + + // replace scalar with negative integer vector + vector = generateInteger(4, true); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntVector(new int[]{-2}, true)}); + assertIndicies(vector, -1, 1, -1, -1); + + // replace scalar with logical scalar + vector = generateInteger(3, true); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), // + new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE}, true)}); + assertIndicies(vector, -1, -1, -1); + + // replace scalar with logical vector + vector = generateInteger(4, true); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), // + new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE}, true)}); + assertIndicies(vector, -1, 1, -1, 3); + + // replace vector indexed by logical vector + vector = generateInteger(4, true); + executeReplace(ElementAccessMode.SUBSET, vector, RDataFactory.createIntVector(new int[]{-1, -2}, true), // + new Object[]{RDataFactory.createLogicalVector(new byte[]{RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE}, true)}); + assertIndicies(vector, -1, 1, -2, 3); + + // replace scalar with integer vector + vector = generateInteger(9, true); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntVector(new int[]{9, 8}, true)}); + assertIndicies(vector, 0, 1, 2, 3, 4, 5, 6, -1, -1); + + // replace scalar with integer scalar + vector = generateInteger(9, true); + executeReplace(ElementAccessMode.SUBSET, vector, RInteger.valueOf(-1), new Object[]{RDataFactory.createIntVector(new int[]{9}, true)}); + assertIndicies(vector, 0, 1, 2, 3, 4, 5, 6, 7, -1); + } + + @Theory + public void testNames(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, true); + RStringVector names = (RStringVector) generateVector(RType.Character, 4, true); + vector.setNames(names); + + RAbstractVector value = generateVector(targetType, 4, true); + RStringVector valueNames = (RStringVector) generateVector(RType.Character, 4, true); + value.setNames(valueNames); + + RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, value, RLogical.TRUE); + + RStringVector newNames = result.getNames(RAttributeProfiles.create()); + assertThat(newNames.getLength(), is(names.getLength())); + assertThat(newNames.getDataAt(0), is(names.getDataAt(0))); + assertThat(newNames.getDataAt(1), is(names.getDataAt(1))); + assertThat(newNames.getDataAt(2), is(names.getDataAt(2))); + assertThat(newNames.getDataAt(3), is(names.getDataAt(3))); + } + + @Theory + public void testCompletenessAfterReplace(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, false); + RAbstractVector replaceWith = generateVector(targetType, 1, true); + + assumeThat(vector.isComplete(), is(false)); + RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, replaceWith, RInteger.valueOf(1)); + assertThat(result.isComplete(), is(false)); + } + + @Theory + public void testCompletenessAfterReplaceAll(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, false); + RAbstractVector replaceWith = generateVector(targetType, 1, true); + + assumeThat(vector.isComplete(), is(false)); + executeReplace(ElementAccessMode.SUBSET, vector, replaceWith, RLogical.valueOf(true)); + + // TODO we would need to find out if we replace all elements. we should support this. + // assertThat(result.isComplete(), is(true)); + } + + @Theory + public void testCompletenessPositionNA(RType targetType) { + RAbstractVector vector = generateVector(targetType, 4, true); + RAbstractVector replaceWith = generateVector(targetType, 1, true); + + RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, replaceWith, RLogical.NA); + + assertThat(result.isComplete(), is(true)); + } + + @Theory + public void testCompletenessOutOfBounds(RType targetType) { + assumeTrue(targetType != RType.Raw); + RAbstractVector vector = generateVector(targetType, 4, true); + RAbstractVector replaceWith = generateVector(targetType, 1, true); + + RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, replaceWith, RInteger.valueOf(10)); + + assertThat(result.isComplete(), is(false)); + } + + @Theory + public void testCasts(RType targetType, RType valueType) { + if (targetType != valueType) { + assumeTrue(targetType != RType.Raw && valueType != RType.Raw); + } + RType resultType = RType.maxPrecedence(targetType, valueType); + + RAbstractVector vector = generateVector(targetType, 4, true); + RAbstractVector value = generateVector(valueType, 4, true); + + RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, value, accessFirst); + assertThat(result.getRType(), is(resultType)); + } + + @Theory + public void testSubsetSingleDimensionTheory(RType targetType, RAbstractVector position) { + assumeTrue(position.getLength() <= 4); + assumeTrue(position.getLength() >= 1); + assumeTrue(position.isComplete()); + + RAbstractVector vector = generateVector(targetType, 4, true); + RAbstractVector value = generateVector(targetType, 4, true); + + RAbstractVector result = executeReplace(ElementAccessMode.SUBSET, vector, value, position); + assertThat(result, is(sameInstance(vector))); + } + + @Theory + public void testSubscriptSingleDimensionTheory(RType targetType, RAbstractVector position) { + assumeTrue(position.getLength() == 1); + if (position instanceof RAbstractIntVector) { + assumeTrue(((RAbstractIntVector) position).getDataAt(0) > 0); + } + + RAbstractVector vector = generateVector(targetType, 4, true); + RAbstractVector value = generateVector(targetType, 1, true); + + executeReplace(ElementAccessMode.SUBSCRIPT, vector, value, position); + + } + + private NodeHandle<ReplaceVectorNode> handle; + private NodeHandle<BoxPrimitiveNode> box; + private ElementAccessMode currentMode; + + @Before + public void setUp() { + handle = null; + } + + @After + public void tearDown() { + handle = null; + } + + private RAbstractVector executeReplace(ElementAccessMode mode, Object vector, Object value, Object... positions) { + if (handle == null || this.currentMode != mode) { + handle = create(mode); + this.currentMode = mode; + } + if (box == null) { + box = createHandle(BoxPrimitiveNodeGen.create(), (node, args) -> node.execute(args[0])); + } + + return (RAbstractVector) box.call(handle.call(vector, positions, value)); + } + + private static NodeHandle<ReplaceVectorNode> create(ElementAccessMode mode) { + return createHandle(ReplaceVectorNode.create(mode), // + (node, args) -> node.apply(args[0], (Object[]) args[1], args[2])); + } + +} diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringCompareNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringCompareNodeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c07140429a02cb6a188c03b0ea57a06e07bf51e0 --- /dev/null +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringCompareNodeTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import static com.oracle.truffle.r.nodes.test.TestUtilities.*; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.*; +import static org.junit.Assume.*; + +import org.junit.*; +import org.junit.experimental.theories.*; +import org.junit.runner.*; + +import com.oracle.truffle.r.nodes.access.vector.SearchFirstStringNode.CompareStringNode; +import com.oracle.truffle.r.nodes.test.*; +import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle; +import com.oracle.truffle.r.runtime.*; + +@RunWith(Theories.class) +public class StringCompareNodeTest extends TestBase { + + // Please note that "FB" and "Ea" produce a hash collision. Thats why its tested here. + @DataPoints public static String[] TEST = {RRuntime.STRING_NA, "a", "abc", "bc".intern(), "bc".intern(), "FB", "Ea"}; + + @Theory + public void testExactNA(String a, String b) { + assumeTrue(a == RRuntime.STRING_NA || b == RRuntime.STRING_NA); + try { + executeCompare(true, a, b); + Assert.fail(); + } catch (AssertionError e) { + } + } + + @Theory + public void testNonExactNA(String a, String b) { + assumeTrue(a == RRuntime.STRING_NA || b == RRuntime.STRING_NA); + try { + executeCompare(false, a, b); + Assert.fail(); + } catch (AssertionError e) { + } + } + + @Theory + public void testExact(String a, String b) { + assumeFalse(a == RRuntime.STRING_NA); + assumeFalse(b == RRuntime.STRING_NA); + assertThat(executeCompare(true, a, b), is(a.equals(b))); + } + + @Theory + public void testNonExact(String a, String b) { + assumeFalse(a == RRuntime.STRING_NA); + assumeFalse(b == RRuntime.STRING_NA); + assertThat(executeCompare(false, a, b), is(a.startsWith(b))); + } + + private static boolean executeCompare(boolean exact, String a, String b) { + NodeHandle<CompareStringNode> handle = createHandle(exact ? CompareStringNode.createEquals() : CompareStringNode.createStartsWith(), // + (node, args) -> node.executeCompare((String) args[0], (String) args[1])); + return (Boolean) handle.call(a, b); + } + +} diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringSearchNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringSearchNodeTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bdec2bec68a4f90f65276f5b9d65f7cf3a91c7c5 --- /dev/null +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/StringSearchNodeTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import static com.oracle.truffle.r.nodes.test.TestUtilities.*; +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.*; + +import org.junit.experimental.theories.*; +import org.junit.runner.*; + +import com.oracle.truffle.r.nodes.test.*; +import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +@RunWith(Theories.class) +public class StringSearchNodeTest extends TestBase { + + // Please note that "FB" and "Ea" produce a hash collision. Thats why its tested here. + @DataPoints public static String[] TEST = {RRuntime.STRING_NA, "a", "abc", "bc".intern(), "bc".intern(), "FB", "Ea"}; + + @Theory + public void testTheory(String aString, String bString, String cString) { + create(); + + RAbstractStringVector a; + RAbstractStringVector b; + + a = createVector(aString); + b = createVector(bString); + assertResult(a, b, executeSearch(a, b)); + + a = createVector(aString, bString); + b = createVector(cString); + assertResult(a, b, executeSearch(a, b)); + + a = createVector(aString); + b = createVector(bString, cString); + assertResult(a, b, executeSearch(a, b)); + + a = createVector(aString, bString); + b = createVector(bString, cString); + assertResult(a, b, executeSearch(a, b)); + } + + private static RAbstractStringVector createVector(String... elements) { + boolean complete = true; + for (int i = 0; i < elements.length; i++) { + if (elements[i] == RRuntime.STRING_NA) { + complete = false; + break; + } + } + return RDataFactory.createStringVector(elements, complete); + } + + private static void assertResult(RAbstractStringVector a, RAbstractStringVector b, RAbstractIntVector result) { + assertThat(result.getLength(), is(b.getLength())); + for (int i = 0; i < b.getLength(); i++) { + int resultIndex = result.getDataAt(i); + String element = b.getDataAt(i); + if (element == RRuntime.STRING_NA) { + assertThat(resultIndex > a.getLength(), is(true)); + continue; + } + if (resultIndex > a.getLength()) { + for (int j = 0; j < a.getLength(); j++) { + assertCompare(false, a.getDataAt(j), element); + } + } else { + for (int j = 0; j < resultIndex - 1; j++) { + assertCompare(false, a.getDataAt(j), element); + } + assertCompare(true, a.getDataAt(resultIndex - 1), element); + } + } + } + + private static void assertCompare(boolean expectedResult, String target, String element) { + boolean actualResult; + actualResult = target.equals(element); + assertThat(actualResult, is(expectedResult)); + } + + private NodeHandle<SearchFirstStringNode> handle; + + private void create() { + handle = createHandle(SearchFirstStringNode.createNode(true, false), // + (node, args) -> { + RAbstractStringVector target = (RAbstractStringVector) args[0]; + return node.apply(target, (RAbstractStringVector) args[1], target.getLength()); + }); + } + + private RAbstractIntVector executeSearch(RAbstractStringVector a, RAbstractStringVector b) { + return (RAbstractIntVector) handle.call(a, b); + } +} diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestBase.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestBase.java index a5fd1823e58a6c3a09881568ed13dc0dfb2739d7..cd70ea2ef56c46787d512371ab993f5f9ff7558d 100644 --- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestBase.java +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestBase.java @@ -24,21 +24,21 @@ package com.oracle.truffle.r.nodes.test; import org.junit.*; -import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.api.vm.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.test.generate.*; public class TestBase { - private static RContext testContext; + private static TruffleVM testVM; @BeforeClass public static void setupClass() { - testContext = FastRSession.create().createTestContext(); + testVM = FastRSession.create().createTestContext(); } @AfterClass public static void finishClass() { - testContext.destroy(); + RContext.destroyContext(testVM); } - } diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java index 6708b1ff9bd109b7df9248e041d9dfcc328e6b99..d13c857a8154c7f0b18550c2726f749650fa32b7 100644 --- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java @@ -26,9 +26,116 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; public class TestUtilities { + private static final int NA_INDEX = 3; + + public static RAbstractVector generateVector(RType type, int size, boolean complete) { + switch (type) { + case Raw: + return generateRaw(size); + case Logical: + return generateLogical(size, complete); + case Integer: + return generateInteger(size, complete); + case Double: + return generateDouble(size, complete); + case Complex: + return generateComplex(size, complete); + case Character: + return generateCharacter(size, complete); + case List: + return generateList(size, complete); + default: + throw new AssertionError(); + + } + } + + private static RAbstractVector generateRaw(int size) { + byte[] array = new byte[size]; + for (int i = 0; i < size; i++) { + array[i] = (byte) i; + } + return RDataFactory.createRawVector(array); + } + + public static RAbstractVector generateLogical(int size, boolean complete) { + byte[] array = new byte[size]; + for (int i = 0; i < size; i++) { + int mode; + if (complete) { + mode = i % 2; + } else { + mode = i % 3; + } + byte value; + switch (mode) { + case 0: + value = RRuntime.LOGICAL_FALSE; + break; + case 1: + value = RRuntime.LOGICAL_TRUE; + break; + case 2: + value = RRuntime.LOGICAL_NA; + break; + default: + throw new AssertionError(); + + } + array[i] = value; + } + return RDataFactory.createLogicalVector(array, complete || !complete && size < 3); + } + + public static RAbstractIntVector generateInteger(int size, boolean complete) { + int[] array = new int[size]; + for (int i = 0; i < size; i++) { + array[i] = !complete && i % (NA_INDEX + 1) == NA_INDEX ? RRuntime.INT_NA : i; + } + return RDataFactory.createIntVector(array, complete || !complete && size < 3); + } + + public static RAbstractDoubleVector generateDouble(int size, boolean complete) { + double[] array = new double[size]; + for (int i = 0; i < size; i++) { + array[i] = !complete && i % (NA_INDEX + 1) == NA_INDEX ? RRuntime.DOUBLE_NA : i; + } + return RDataFactory.createDoubleVector(array, complete || !complete && size < 3); + } + + public static RAbstractVector generateComplex(int size, boolean complete) { + double[] array = new double[size << 1]; + for (int i = 0; i < size; i++) { + boolean useNA = !complete && i % (NA_INDEX + 1) == NA_INDEX; + array[i << 1 - 1] = useNA ? RComplex.NA.getRealPart() : i; + array[i << 1] = useNA ? RComplex.NA.getRealPart() : i; + } + return RDataFactory.createComplexVector(array, complete || !complete && size < NA_INDEX); + } + + public static RAbstractVector generateCharacter(int size, boolean complete) { + String[] array = new String[size]; + for (int i = 0; i < size; i++) { + array[i] = !complete && i % (NA_INDEX + 1) == NA_INDEX ? RRuntime.STRING_NA : String.valueOf(i); + } + return RDataFactory.createStringVector(array, complete || !complete && size < 3); + } + + public static RAbstractVector generateList(int size, boolean complete) { + Object[] array = new Object[size]; + for (int i = 0; i < size; i++) { + array[i] = !complete && i % (NA_INDEX + 1) == NA_INDEX ? RNull.instance : i; + } + RAbstractVector list = RDataFactory.createList(array); + list.setComplete(complete || !complete && size < 3); + return list; + } + /** * Creates a handle that emulates the behavior as if this node would be executed inside of an R * call. diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java index 93878456188ae531108e22fa26eaeb68081294de..55951561911d766208f536f1046d25fc44ff0c9e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/InlineCacheNode.java @@ -30,8 +30,7 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.function.*; -import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.Engine; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.nodes.*; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java index c9a489d5b2cbb3a6fd6a643bd518738d93bb5021..b9ca0a89a7fb8082e0ac3e6db1b4ddae20a177b3 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java @@ -31,6 +31,7 @@ import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.builtin.*; import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.env.frame.*; /** @@ -48,7 +49,7 @@ public abstract class RRootNode extends RootNode implements HasSignature { private final FormalArguments formalArguments; protected RRootNode(SourceSection src, FormalArguments formalArguments, FrameDescriptor frameDescriptor) { - super(src, frameDescriptor); + super(RContext.getEngine().getTruffleLanguage(), src, frameDescriptor); this.formalArguments = formalArguments; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessFieldNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessFieldNode.java index 902871070eb226f019ba09c9315d61338b4bfdad..e048c6b2421965591c0d45c1ad71568b7d205fdf 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessFieldNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessFieldNode.java @@ -26,6 +26,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateFieldNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateFieldNode.java index caf62eb740e684319aaa0b5e363e019b96a97e6c..651c5389ca4b04bc2c16088d61ae9c952cea7a6f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateFieldNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateFieldNode.java @@ -30,6 +30,7 @@ import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.unary.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java index 39ddd42562463a36f6ed0825c55169e532070588..19e7829ec14a2dc1f264e51f3726d74ba6e3b22d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteLocalFrameVariableNode.java @@ -91,7 +91,6 @@ public abstract class WriteLocalFrameVariableNode extends BaseWriteVariableNode FrameSlot frameSlot = findOrAddFrameSlot(frame.getFrameDescriptor(), getName(), initialKind); replace(ResolvedWriteLocalFrameVariableNode.create(getRhs(), getName(), frameSlot, getMode())).execute(frame, value); } - } @NodeFields({@NodeField(name = "frameSlot", type = FrameSlot.class), @NodeField(name = "mode", type = Mode.class)}) diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java index f617962d14a72e7cb5fd8897dbe3e4c3d69a32d0..2dfb0525963bb6fe2b230a35cd649d1c1f837a45 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/WriteVariableNode.java @@ -62,7 +62,7 @@ public abstract class WriteVariableNode extends RNode { * Variant for saving function arguments, i.e. from {@link RArguments} into the frame. */ public static WriteVariableNode createArgSave(String name, RNode rhs) { - if (FastROptions.InvisibleArgs.getValue()) { + if (FastROptions.InvisibleArgs) { return WriteLocalFrameVariableNode.create(name, rhs, Mode.INVISIBLE); } else { return WriteLocalFrameVariableNode.create(name, rhs, Mode.REGULAR); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java index 8e613c8b1f94caa0d17221a8aefd544eb21f6976..f07ebf185e07763313af6d7a393a882eaa9c1739 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java @@ -561,7 +561,7 @@ public final class ReadVariableNode extends RNode implements RSyntaxNode, Visibi lastLevel = new MultiAssumptionLevel(lastLevel, assumptions.toArray(new Assumption[assumptions.size()])); } - if (FastROptions.PrintComplexLookups.getValue() && levels.size() > 1 && complex) { + if (FastROptions.PrintComplexLookups && levels.size() > 1 && complex) { System.out.println(identifier + " " + lastLevel); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java new file mode 100644 index 0000000000000000000000000000000000000000..24d2e0dabadfc4c0c6564b49055e81c246817dc1 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.access.vector.CachedExtractVectorNodeFactory.SetNamesNodeGen; +import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile; +import com.oracle.truffle.r.nodes.profile.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.env.*; +import com.oracle.truffle.r.runtime.nodes.*; + +final class CachedExtractVectorNode extends CachedVectorNode { + + protected static final boolean DEFAULT_EXACT = true; + protected static final boolean DEFAULT_DROP_DIMENSION = true; + + private final Class<? extends RTypedValue> targetClass; + private final Class<? extends RTypedValue> exactClass; + private final Class<? extends RTypedValue> dropDimensionsClass; + private final boolean exact; + private final boolean dropDimensions; + + private final VectorLengthProfile vectorLengthProfile = VectorLengthProfile.create(); + private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create(); + + @Child private WriteIndexedVectorNode writeVectorNode; + @Child private PositionsCheckNode positionsCheckNode; + @Child private SetNamesNode setNamesNode; + @Children private final CachedExtractVectorNode[] extractNames; + + @Child private ExtractDimNamesNode extractDimNames; + + private final ConditionProfile resultHasDimensions = ConditionProfile.createBinaryProfile(); + + /** + * Profile if any metadata was applied at any point in time. This is useful extract primitive + * values from the result in case no metadata was ever applied. + */ + private final BranchProfile metadataApplied = BranchProfile.create(); + + public CachedExtractVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, RTypedValue exact, RTypedValue dropDimensions, boolean recursive) { + super(mode, vector, positions, recursive); + this.targetClass = vector.getClass(); + this.exactClass = exact.getClass(); + this.dropDimensionsClass = dropDimensions.getClass(); + Object[] convertedPositions = filterPositions(positions); + this.extractNames = new CachedExtractVectorNode[convertedPositions.length]; + this.exact = logicalAsBoolean(exact, DEFAULT_EXACT); + this.dropDimensions = logicalAsBoolean(dropDimensions, DEFAULT_DROP_DIMENSION); + this.positionsCheckNode = new PositionsCheckNode(mode, vectorType, convertedPositions, this.exact, false, recursive); + if (vectorType != RType.Null && vectorType != RType.Environment) { + this.writeVectorNode = WriteIndexedVectorNode.create(vectorType, convertedPositions.length, true, false, false); + } + } + + public boolean isSupported(Object target, Object[] positions, Object exactValue, Object dropDimensionsValue) { + if (targetClass == target.getClass() && exactValue.getClass() == this.exactClass // + && logicalAsBoolean(dropDimensionsClass.cast(dropDimensionsValue), DEFAULT_DROP_DIMENSION) == this.dropDimensions // + && dropDimensionsValue.getClass() == this.dropDimensionsClass // + && logicalAsBoolean(exactClass.cast(exactValue), DEFAULT_EXACT) == this.exact) { + return positionsCheckNode.isSupported(positions); + } + return false; + } + + public Object apply(Object originalVector, Object[] originalPositions, PositionProfile[] originalProfiles, Object originalExact, Object originalDropDimensions) { + final Object[] positions = filterPositions(originalPositions); + + assert isSupported(originalVector, positions, originalExact, originalDropDimensions); + + final RTypedValue castVector = targetClass.cast(originalVector); + final RAbstractContainer vector; + switch (vectorType) { + case Null: + return RNull.instance; + case Environment: + /* + * TODO (chumer) the environment case cannot be applied to the default extract + * method as it does not implement RAbstractContainer. This should be harmonized + * later. + */ + return doEnvironment((REnvironment) castVector, positions); + case Integer: + if (castVector instanceof RFactor) { + vector = ((RFactor) castVector).getVector(); + break; + } + default: + vector = (RAbstractContainer) castVector; + } + + int vectorLength = vectorLengthProfile.profile(vector.getLength()); + + int[] dimensions = getDimensions(vector); + + PositionProfile[] positionProfiles; + if (originalProfiles == null) { + positionProfiles = positionsCheckNode.executeCheck(vector, dimensions, vectorLength, positions); + } else { + positionProfiles = originalProfiles; + } + + if (isMissingSingleDimension()) { + // special case for x<-matrix(1:4, ncol=2); x[] + return originalVector; + } + + int extractedVectorLength = positionsCheckNode.getSelectedPositionsCount(positionProfiles); + final RAbstractVector extractedVector; + switch (vectorType) { + case Language: + case DataFrame: + case Expression: + case PairList: + extractedVector = RType.List.create(extractedVectorLength, false); + break; + default: + extractedVector = vectorType.create(extractedVectorLength, false); + break; + } + + if (mode.isSubset()) { + if (extractedVectorLength > 0) { + writeVectorNode.enableValueNACheck(vector); + writeVectorNode.apply(extractedVector, extractedVectorLength, positions, vector, vectorLength, dimensions); + extractedVector.setComplete(writeVectorNode.neverSeenNAInValue()); + RNode.reportWork(this, extractedVectorLength); + } + if (numberOfDimensions == 1) { + // names only need to be considered for single dimensional accesses + RStringVector originalNames = vector.getNames(vectorNamesProfile); + if (originalNames != null) { + metadataApplied.enter(); + setNames(extractedVector, extractNames(originalNames, positions, positionProfiles, 0, originalExact, originalDropDimensions)); + } + } else { + assert numberOfDimensions > 1; + applyDimensions(vector, extractedVector, extractedVectorLength, positionProfiles, positions); + } + + switch (vectorType) { + case Expression: + return new RExpression((RList) extractedVector); + case Language: + return materializeLanguage(extractedVector); + default: + return trySubsetPrimitive(extractedVector); + } + + } else { + writeVectorNode.apply(extractedVector, extractedVectorLength, positions, vector, vectorLength, dimensions); + RNode.reportWork(this, 1); + assert extractedVectorLength == 1; + return extractedVector.getDataAtAsObject(0); + } + } + + private int[] getDimensions(final RAbstractContainer vector) { + int[] dimensions; + if (numberOfDimensions == 1) { + dimensions = null; + } else { + dimensions = loadVectorDimensions(vector); + } + return dimensions; + } + + private Object trySubsetPrimitive(RAbstractVector extractedVector) { + if (!metadataApplied.isVisited() && positionsCheckNode.getCachedSelectedPositionsCount() == 1 && !isList()) { + /* + * If the selected count was always 1 and no metadata was ever set we can just extract + * the primitive value from the vector. This branch has to fold to a constant because we + * want to avoid the toggling of the return types depending on input values. + */ + assert extractedVector.getNames(RAttributeProfiles.create()) == null; + assert extractedVector.getDimensions() == null; + assert extractedVector.getDimNames(null) == null; + return extractedVector.getDataAtAsObject(0); + } + return extractedVector; + } + + private Object doEnvironment(REnvironment env, Object[] positions) { + if (mode.isSubset()) { + errorBranch.enter(); + throw RError.error(this, RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName()); + } + + String positionString = tryCastSingleString(positionsCheckNode, positions); + if (positionString != null) { + Object obj = env.get(positionString); + return obj == null ? RNull.instance : obj; + } + errorBranch.enter(); + throw RError.error(this, RError.Message.WRONG_ARGS_SUBSET_ENV); + } + + private boolean isMissingSingleDimension() { + return numberOfDimensions == 1 && positionsCheckNode.isMissing(); + } + + @TruffleBoundary + private static Object materializeLanguage(RAbstractVector extractedVector) { + return RContext.getRRuntimeASTAccess().fromList(extractedVector); + } + + private Object extract(int dimensionIndex, RTypedValue vector, Object pos, PositionProfile profile) { + if (extractDimNames == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + extractDimNames = new ExtractDimNamesNode(numberOfDimensions); + } + return extractDimNames.extract(dimensionIndex, vector, pos, profile); + } + + private boolean isList() { + return vectorType == RType.List; + } + + private final NullProfile dimNamesNull = NullProfile.create(); + + @ExplodeLoop + private void applyDimensions(RAbstractContainer originalTarget, RAbstractVector extractedTarget, int extractedTargetLength, PositionProfile[] positionProfile, Object[] positions) { + // TODO speculate on the number of counted dimensions + int dimCount = countDimensions(positionProfile); + + int[] newDimensions = new int[dimCount]; + RList originalDimNames = dimNamesNull.profile(originalTarget.getDimNames(null)); + Object[] newDimNames = null; + if (originalDimNames != null) { + newDimNames = new Object[dimCount]; + } + + int dimIndex = -1; + for (int i = 0; i < numberOfDimensions; i++) { + int selectedPositionsCount = positionProfile[i].selectedPositionsCount; + if (selectedPositionsCount != 1 || !dropDimensions) { + dimIndex++; + newDimensions[dimIndex] = selectedPositionsCount; + if (originalDimNames != null) { + Object dataAt = originalDimNames.getDataAt(i); + newDimNames[dimIndex] = extract(i, (RTypedValue) dataAt, positions[i], positionProfile[i]); + } + } + } + + if (resultHasDimensions.profile(dimCount > 1)) { + metadataApplied.enter(); + extractedTarget.setDimensions(newDimensions); + if (newDimNames != null) { + extractedTarget.setDimNames(RDataFactory.createList(newDimNames)); + } + } else if (originalDimNames != null && originalDimNames.getLength() > 0) { + RAbstractStringVector foundNames = translateDimNamesToNames(positionProfile, originalDimNames, extractedTargetLength, positions); + if (foundNames != null && foundNames.getLength() > 0) { + metadataApplied.enter(); + setNames(extractedTarget, foundNames); + } + } + } + + @ExplodeLoop + private int countDimensions(PositionProfile[] boundsProfile) { + int dimCount = 0; + for (int i = 0; i < numberOfDimensions; i++) { + int selectedPositionsCount = boundsProfile[i].selectedPositionsCount; + if (selectedPositionsCount != 1 || !dropDimensions) { + dimCount++; + } + } + return dimCount; + } + + @ExplodeLoop + private RAbstractStringVector translateDimNamesToNames(PositionProfile[] positionProfile, RList originalDimNames, int newVectorLength, Object[] positions) { + RAbstractStringVector foundNames = null; + for (int currentDimIndex = numberOfDimensions - 1; currentDimIndex >= 0; currentDimIndex--) { + PositionProfile profile = positionProfile[currentDimIndex]; + if (profile.selectedPositionsCount != newVectorLength) { + continue; + } + + Object srcNames = originalDimNames.getDataAt(currentDimIndex); + if (srcNames != RNull.instance) { + Object position = positions[currentDimIndex]; + + Object newNames = extractNames((RAbstractStringVector) srcNames, new Object[]{position}, new PositionProfile[]{profile}, currentDimIndex, RLogical.valueOf(true), + RLogical.valueOf(dropDimensions)); + if (newNames != RNull.instance) { + if (newNames instanceof String) { + newNames = RDataFactory.createStringVector((String) newNames); + } + RAbstractStringVector castFoundNames = (RAbstractStringVector) newNames; + if (castFoundNames.getLength() == newVectorLength) { + if (foundNames != null) { + /* + * the idea here is that you can get names from dimnames only if the + * name of of an item can be unambiguously identified (there can be only + * one matching name in all dimensions - if "name" has already been set, + * we might as well return null already) + */ + foundNames = null; + break; + } + foundNames = (RAbstractStringVector) newNames; + } + } + + } + } + return foundNames; + } + + private Object extractNames(RAbstractStringVector originalNames, Object[] positions, PositionProfile[] profiles, int dimension, Object originalExact, Object originalDropDimensions) { + if (extractNames[dimension] == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + extractNames[dimension] = insert(new CachedExtractVectorNode(mode, originalNames, positions, (RTypedValue) originalExact, (RTypedValue) originalDropDimensions, recursive)); + } + assert extractNames[dimension].isSupported(originalNames, positions, originalExact, originalDropDimensions); + Object newNames = extractNames[dimension].apply(originalNames, positions, profiles, originalExact, originalDropDimensions); + return newNames; + } + + private void setNames(RAbstractContainer vector, Object newNames) { + if (setNamesNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + setNamesNode = insert(SetNamesNodeGen.create()); + } + setNamesNode.execute(vector, newNames); + } + + protected abstract static class SetNamesNode extends Node { + + public abstract void execute(RAbstractContainer container, Object newNames); + + @Specialization + protected void setNames(RAbstractContainer container, RAbstractStringVector newNames) { + container.setNames(newNames.materialize()); + } + + @Specialization + protected void setNames(RAbstractContainer container, String newNames) { + container.setNames(RString.valueOf(newNames).materialize()); + } + + @Specialization + protected void setNames(RAbstractContainer container, @SuppressWarnings("unused") RNull newNames) { + container.setNames(null); + } + + } + + private static class ExtractDimNamesNode extends Node { + + @Children private final CachedExtractVectorNode[] extractNodes; + + public ExtractDimNamesNode(int dimensions) { + this.extractNodes = new CachedExtractVectorNode[dimensions]; + } + + public Object extract(int dimensionIndex, RTypedValue vector, Object position, PositionProfile profile) { + Object[] positions = new Object[]{position}; + PositionProfile[] profiles = new PositionProfile[]{profile}; + if (extractNodes[dimensionIndex] == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + extractNodes[dimensionIndex] = insert(new CachedExtractVectorNode(ElementAccessMode.SUBSET, vector, positions, RLogical.TRUE, RLogical.TRUE, false)); + } + CompilerAsserts.partialEvaluationConstant(dimensionIndex); + assert extractNodes[dimensionIndex].isSupported(vector, positions, RLogical.TRUE, RLogical.TRUE); + return extractNodes[dimensionIndex].apply(vector, positions, profiles, RLogical.TRUE, RLogical.TRUE); + } + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java new file mode 100644 index 0000000000000000000000000000000000000000..1cf1800a2a8a1e89e7720d45ff14cb642489a115 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile; +import com.oracle.truffle.r.nodes.binary.*; +import com.oracle.truffle.r.nodes.profile.*; +import com.oracle.truffle.r.nodes.unary.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.env.*; +import com.oracle.truffle.r.runtime.nodes.*; + +final class CachedReplaceVectorNode extends CachedVectorNode { + + private static final Object DELETE_MARKER = new Object(); + + private final Class<?> vectorClass; + private final Class<?> valueClass; + + private final VectorLengthProfile targetLengthProfile = VectorLengthProfile.create(); + private final VectorLengthProfile valueLengthProfile = VectorLengthProfile.create(); + private final BranchProfile warningBranch = BranchProfile.create(); + private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create(); + private final RAttributeProfiles positionNamesProfile = RAttributeProfiles.create(); + private final ConditionProfile rightIsShared = ConditionProfile.createBinaryProfile(); + private final BranchProfile rightIsNonTemp = BranchProfile.create(); + private final BranchProfile rightIsTemp = BranchProfile.create(); + private final BranchProfile resizeProfile = BranchProfile.create(); + private final BranchProfile sharedProfile = BranchProfile.create(); + + private final RType valueType; + private final RType castType; + private final boolean updatePositionNames; + + @Child private WriteIndexedVectorNode writeVectorNode; + @Child private PositionsCheckNode positionsCheckNode; + @Child private CastNode castVectorNode; + @Child private CachedReplaceVectorNode copyPositionNames; + @Child private DeleteElementsNode deleteElementsNode; + + public CachedReplaceVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, RTypedValue value, boolean updatePositionNames, boolean recursive) { + super(mode, vector, positions, recursive); + + this.updatePositionNames = updatePositionNames; + this.vectorClass = vector.getClass(); + this.valueClass = value.getClass(); + this.valueType = value.getRType(); + this.castType = verifyCastType(resolveCastVectorType()); + this.castVectorNode = createCastVectorNode(); + this.deleteElementsNode = isDeleteElements() ? new DeleteElementsNode() : null; + + Object[] convertedPositions = filterPositions(positions); + this.positionsCheckNode = new PositionsCheckNode(mode, vectorType, convertedPositions, true, true, recursive); + if (castType != null && !castType.isNull()) { + this.writeVectorNode = WriteIndexedVectorNode.create(castType, convertedPositions.length, false, true, mode.isSubscript() && !isDeleteElements()); + } + } + + public boolean isSupported(Object target, Object[] positions, Object values) { + if (vectorClass == target.getClass() && values.getClass() == valueClass) { + return positionsCheckNode.isSupported(positions); + } + return false; + } + + public Object apply(Object originalVector, Object[] originalPositions, Object originalValues) { + final Object[] positions = filterPositions(originalPositions); + assert isSupported(originalVector, positions, originalValues); + + Object castVector = vectorClass.cast(originalVector); + Object castValue = valueClass.cast(originalValues); + + RAbstractContainer value; + if (valueType == RType.Null) { + if (vectorType == RType.Null) { + // we cast Null to Logical, but in the end it will fold and return Null + value = RType.Logical.getEmpty(); + } else if (castType == RType.List) { + value = RDataFactory.createList(new Object[]{DELETE_MARKER}); + } else { + value = castType.getEmpty(); + } + } else { + if (!isList() && castValue instanceof RFactor) { + value = ((RFactor) castValue).getVector(); + } else { + value = (RAbstractContainer) castValue; + } + } + + int appliedValueLength = valueLengthProfile.profile(value.getLength()); + + int valueLength; + if (this.numberOfDimensions > 1 && isDeleteElements()) { + valueLength = 0; + } else { + valueLength = appliedValueLength; + } + + if (vectorType == RType.Null) { + if (valueLength == 0) { + return RNull.instance; + } + } + + /* + * Unfortunately special behavior for some RTypes are necessary. We should aim for getting + * rid of them as much as possible in the future. + */ + RAbstractVector vector; + switch (vectorType) { + case Null: + vector = castType.getEmpty(); + break; + case Factor: + vector = ((RFactor) castVector).getVector(); + break; + case PairList: + vector = ((RPairList) castVector).toRList(); + break; + case Environment: + return doEnvironment((REnvironment) castVector, positions, castValue); + case Language: + vector = RContext.getRRuntimeASTAccess().asList((RLanguage) castVector); + break; + case Expression: + vector = ((RExpression) castVector).getList(); + break; + default: + vector = (RAbstractVector) castVector; + break; + } + + int vectorLength = targetLengthProfile.profile(vector.getLength()); + int[] vectorDimensions; + if (numberOfDimensions == 1) { + /* For single dimension case we never need to load the dimension. */ + vectorDimensions = null; + } else { + assert numberOfDimensions > 1; + vectorDimensions = loadVectorDimensions(vector); + } + + PositionProfile[] positionProfiles = positionsCheckNode.executeCheck(vector, vectorDimensions, vectorLength, positions); + + if (castVectorNode != null) { + vector = (RAbstractVector) castVectorNode.execute(vector); + } + + int replacementLength = positionsCheckNode.getSelectedPositionsCount(positionProfiles); + if (replacementLength == 0) { + /* Nothing to modify */ + return vector; + } + + if (valueLength != 1) { + verifyValueLength(positionProfiles, valueLength); + } + + if (isList()) { + if (mode.isSubscript()) { + value = copyValueOnAssignment(value); + } + } else if (value instanceof RAbstractVector) { + value = ((RAbstractVector) value).castSafe(castType); + } + + vector = share(vector); + + int maxOutOfBounds = positionsCheckNode.getMaxOutOfBounds(positionProfiles); + if (maxOutOfBounds > vectorLength) { + resizeProfile.enter(); + if (isDeleteElements() && mode.isSubscript()) { + return vector; + } + vector = resizeVector(vector, maxOutOfBounds); + vectorLength = maxOutOfBounds; + } + vector = vector.materialize(); + + vectorLength = targetLengthProfile.profile(vector.getLength()); + + if (mode.isSubset()) { + /* + * Interestingly we always need to provide not NOT_MULTIPLE_REPLACEMENT error messages + * for multi-dimensional deletes. + */ + if ((this.numberOfDimensions > 1 && isDeleteElements()) || replacementLength % valueLength != 0) { + if (this.numberOfDimensions > 1) { + errorBranch.enter(); + throw RError.error(this, RError.Message.NOT_MULTIPLE_REPLACEMENT); + } else { + warningBranch.enter(); + RError.warning(this, RError.Message.NOT_MULTIPLE_REPLACEMENT); + } + } + } + + writeVectorNode.enableValueNACheck(value); + writeVectorNode.apply(vector, vectorLength, positions, value, appliedValueLength, vectorDimensions); + vector.setComplete(vector.isComplete() && writeVectorNode.neverSeenNAInValue()); + RNode.reportWork(this, replacementLength); + + if (isDeleteElements()) { + assert deleteElementsNode != null; + vector = deleteElementsNode.deleteElements(vector, vectorLength); + } else if (this.numberOfDimensions == 1 && updatePositionNames) { + // depth must be == 0 to avoid recursive position name updates + updateVectorWithPositionNames(vector, positions); + } + + return vector; + } + + private RType verifyCastType(RType compatibleType) { + if (compatibleType == null && (vectorType.isNull() || vectorType.isVector())) { + Message message; + if (mode.isSubset()) { + message = RError.Message.SUBASSIGN_TYPE_FIX; + } else { + message = RError.Message.SUBSCRIPT_TYPES; + } + throw RError.error(this, message, valueType.getName(), vectorType.getName(), false); + } + return compatibleType; + } + + private CastNode createCastVectorNode() { + if (castType == vectorType || castType == null || castType == RType.Null) { + return null; + } + /* + * All casts except list casts preserve dimension names. + */ + if (castType == RType.List) { + return CastListNodeGen.create(true, false, true); + } else { + return CastTypeNode.createCast(castType, true, true, true); + } + } + + private boolean isDeleteElements() { + return castType == RType.List && valueClass == RNull.class; + } + + private boolean isList() { + return castType == RType.List; + } + + private RType resolveCastVectorType() { + final RType vector; + // convert type for list like values + switch (this.vectorType) { + case Language: + case DataFrame: + case Expression: + case PairList: + vector = RType.List; + break; + default: + vector = this.vectorType; + break; + } + + RType value = this.valueType; + + if (vector.isVector() && value.isVector()) { + if (vector != value) { + if (vector == RType.List || value == RType.List) { + return RType.List; + } + if (vector == RType.Raw || value == RType.Raw) { + return null; + } + } + return RType.maxPrecedence(value, vector); + } else if (vector.isNull() || value.isNull()) { + if (!value.isNull()) { + return value; + } + if (mode.isSubscript() && numberOfDimensions > 1) { + return null; + } + return vector; + } else { + return null; + } + } + + private void verifyValueLength(PositionProfile[] positionProfiles, int valueLength) { + if (mode.isSubscript()) { + if (!isList()) { + errorBranch.enter(); + if (valueLength == 0) { + throw RError.error(this, RError.Message.REPLACEMENT_0); + } else { + throw RError.error(this, RError.Message.MORE_SUPPLIED_REPLACE); + } + } + } else { + assert mode.isSubset(); + if (valueLength == 0) { + errorBranch.enter(); + throw RError.error(this, RError.Message.REPLACEMENT_0); + } else if (positionsCheckNode.getContainsNA(positionProfiles)) { + errorBranch.enter(); + throw RError.error(this, RError.Message.NA_SUBSCRIPTED); + } + } + } + + private void updateVectorWithPositionNames(RAbstractVector vector, Object[] positions) { + Object position = positionsCheckNode.getPositionCheckAt(0).profilePosition(positions[0]); + RStringVector positionNames; + if (position instanceof RMissing) { + positionNames = null; + } else { + positionNames = ((RAbstractVector) position).getNames(positionNamesProfile); + } + if (positionNames != null && positionNames.getLength() > 0) { + updatePositionNames(vector, positionNames, positions); + } + } + + private Object doEnvironment(REnvironment env, Object[] positions, Object originalValues) { + if (mode.isSubset()) { + errorBranch.enter(); + throw RError.error(this, RError.Message.OBJECT_NOT_SUBSETTABLE, RType.Environment.getName()); + } + + String positionString = tryCastSingleString(positionsCheckNode, positions); + if (positionString != null) { + env.setAttr(positionString.intern(), originalValues); + return originalValues; + } + errorBranch.enter(); + throw RError.error(this, RError.Message.WRONG_ARGS_SUBSET_ENV); + } + + private final ConditionProfile sharedConditionProfile = ConditionProfile.createBinaryProfile(); + + private final ValueProfile sharedClassProfile = ValueProfile.createClassProfile(); + + /* + * TODO (chumer) share code between {@link #share(RAbstractVector)} and {@link + * #copyValueOnAssignment(RAbstractContainer)} + */ + private RAbstractVector share(RAbstractVector vector) { + RAbstractVector returnVector = vector; + if (returnVector instanceof RShareable) { + RShareable shareable = (RShareable) returnVector; + // TODO find out if we need to copy always in the recursive case + if (sharedConditionProfile.profile(shareable.isShared()) || recursive) { + sharedProfile.enter(); + shareable = (RShareable) returnVector.copy(); + returnVector = (RAbstractVector) shareable; + if (FastROptions.NewStateTransition) { + shareable.incRefCount(); + } else if (shareable.isTemporary()) { + shareable.markNonTemporary(); + } + } + } + returnVector = sharedClassProfile.profile(returnVector); + + CompilerAsserts.partialEvaluationConstant(returnVector.getClass()); + + return returnVector; + } + + private RAbstractContainer copyValueOnAssignment(RAbstractContainer value) { + RShareable val = value.materializeToShareable(); + if (FastROptions.NewStateTransition) { + if (rightIsShared.profile(val.isShared())) { + val = val.copy(); + } else { + val.incRefCount(); + } + } else { + if (rightIsShared.profile(val.isShared())) { + val = val.copy(); + } else if (!val.isTemporary()) { + rightIsNonTemp.enter(); + val.makeShared(); + } else { + assert val.isTemporary(); + rightIsTemp.enter(); + val.markNonTemporary(); + } + } + return (RAbstractContainer) val; + } + + // TODO (chumer) this is way to compilicated at the moment + // not yet worth compiling. we should introduce some nodes for this + @TruffleBoundary + private static void copyAttributes(RAbstractVector vector, RList result) { + result.copyRegAttributesFrom(vector); + } + + // TODO (chumer) this is way to complicated at the moment + // its not yet worth compiling it we need a better attribute system + @TruffleBoundary + private RVector resizeVector(RAbstractVector vector, int size) { + RStringVector oldNames = vector.getNames(vectorNamesProfile); + RVector res = vector.copyResized(size, true).materialize(); + if (vector instanceof RVector) { + res.copyAttributesFrom(positionNamesProfile, vector); + } + res.setDimensionsNoCheck(null); + res.setDimNamesNoCheck(null); + if (oldNames != null) { + oldNames = oldNames.resizeWithEmpty(size); + res.setNames(oldNames); + } + return res; + } + + private void updatePositionNames(RAbstractVector resultVector, RAbstractStringVector positionNames, Object[] positions) { + RTypedValue names = resultVector.getNames(positionNamesProfile); + if (names == null) { + String[] emptyVector = new String[resultVector.getLength()]; + Arrays.fill(emptyVector, ""); + names = RDataFactory.createStringVector(emptyVector, true); + } + if (copyPositionNames == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + copyPositionNames = insert(new CachedReplaceVectorNode(mode, names, positions, positionNames, false, recursive)); + } + assert copyPositionNames.isSupported(names, positions, positionNames); + RAbstractStringVector newNames = (RAbstractStringVector) copyPositionNames.apply(names, positions, positionNames); + resultVector.setNames(newNames.materialize()); + } + + private static final class DeleteElementsNode extends Node { + + private static final int PREVIOUS_RESULT_GENERIC = -2; + private static final int PREVIOUS_RESULT_UNINITIALIZED = -1; + + /* + * We speculate here for the result length to be always the same. This way we can omit the + * first round of counting deleted elements. + */ + @CompilationFinal private int previousResultLength = PREVIOUS_RESULT_UNINITIALIZED; + + private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create(); + + public RAbstractVector deleteElements(RAbstractVector vector, int vectorLength) { + // we can speculate here that we delete always the same number of elements + // without counting them. + int resultLength; + if (isPreviousResultSpecialized()) { + resultLength = previousResultLength; + } else if (previousResultLength == PREVIOUS_RESULT_UNINITIALIZED) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + resultLength = previousResultLength = calculateResultSize(vector, vectorLength); + } else { + resultLength = calculateResultSize(vector, vectorLength); + } + + Object[] data = new Object[resultLength]; + RStringVector names = vector.getNames(vectorNamesProfile); + boolean hasNames = names != null; + String[] newNames = null; + if (hasNames) { + newNames = new String[resultLength]; + } + + int resultIndex = 0; + for (int i = 0; i < vectorLength; i++) { + Object element = vector.getDataAtAsObject(i); + if (element != DELETE_MARKER) { + data[resultIndex] = element; + if (hasNames) { + newNames[resultIndex] = names.getDataAt(i); + } + resultIndex++; + if (isPreviousResultSpecialized() && resultIndex > resultLength) { + // got too many elements + CompilerDirectives.transferToInterpreterAndInvalidate(); + previousResultLength = PREVIOUS_RESULT_GENERIC; + return deleteElements(vector, vectorLength); + } + } + } + if (isPreviousResultSpecialized() && resultIndex != resultLength) { + // got less elements to delete + CompilerDirectives.transferToInterpreterAndInvalidate(); + previousResultLength = PREVIOUS_RESULT_GENERIC; + return deleteElements(vector, vectorLength); + } + + RStringVector newNamesVector = null; + if (hasNames) { + newNamesVector = RDataFactory.createStringVector(newNames, names.isComplete()); + } + RList result = RDataFactory.createList(data, newNamesVector); + copyAttributes(vector, result); + return result; + } + + private boolean isPreviousResultSpecialized() { + return previousResultLength >= 0; + } + + private static int calculateResultSize(RAbstractVector vector, int vectorLength) { + int resultSize = 0; + for (int i = 0; i < vectorLength; i++) { + if (vector.getDataAtAsObject(i) != DELETE_MARKER) { + resultSize++; + } + } + return resultSize; + } + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java new file mode 100644 index 0000000000000000000000000000000000000000..339fa59bc46ddef4cf423e04b03c9f35ea0af308 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.nodes.*; + +abstract class CachedVectorNode extends RBaseNode { + + protected final ElementAccessMode mode; + protected final RType vectorType; + + /** + * Recursive indicates that the vector node is used inside {@link RecursiveReplaceSubscriptNode} + * or {@link RecursiveExtractSubscriptNode}. + */ + protected final boolean recursive; + + protected final BranchProfile errorBranch = BranchProfile.create(); + protected final int numberOfDimensions; + protected final int filteredPositionsLength; + + @Child private GetDataFrameDimensionNode getDataFrameDimension; + + public CachedVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, boolean recursive) { + this.mode = mode; + this.vectorType = vector.getRType(); + this.recursive = recursive; + this.filteredPositionsLength = initializeFilteredPositionsCount(positions); + if (filteredPositionsLength == -1) { + this.numberOfDimensions = positions.length; + } else { + this.numberOfDimensions = filteredPositionsLength; + } + if (!isSubsetable(vectorType)) { + throw RError.error(this, RError.Message.OBJECT_NOT_SUBSETTABLE, vectorType.getName()); + } + } + + protected int initializeFilteredPositionsCount(Object[] positions) { + int dimensions = 0; + for (int i = 0; i < positions.length; i++) { + // for cases like RMissing the position does not contribute to the number of dimensions + if (!isRemovePosition(positions[i])) { + dimensions++; + } + } + if (positions.length == dimensions || dimensions <= 0) { + return -1; + } else { + return dimensions; + } + } + + @ExplodeLoop + protected Object[] filterPositions(Object[] positions) { + // we assume that the positions count cannot change as the isRemovePosition check + // is just based on types and therefore does not change per position instance. + assert initializeFilteredPositionsCount(positions) == filteredPositionsLength; + if (filteredPositionsLength != -1) { + Object[] newPositions = new Object[filteredPositionsLength]; + int newPositionIndex = 0; + for (int i = 0; i < filteredPositionsLength; i++) { + Object position = positions[i]; + if (!isRemovePosition(position)) { + newPositions[newPositionIndex++] = position; + } + } + return newPositions; + } + return positions; + } + + private static boolean isRemovePosition(Object positions) { + return positions instanceof RMissing; + } + + protected static boolean logicalAsBoolean(RTypedValue cast, boolean defaultValue) { + if (cast instanceof RMissing) { + return defaultValue; + } else { + RAbstractLogicalVector logical = (RAbstractLogicalVector) cast; + if (logical.getLength() == 0) { + return defaultValue; + } else { + return logical.getDataAt(0) == RRuntime.LOGICAL_TRUE; + } + } + } + + private static boolean isSubsetable(RType type) { + if (type.isVector()) { + return true; + } + switch (type) { + case Null: + case Language: + case Factor: + case PairList: + case Environment: + case Expression: + return true; + default: + return false; + } + } + + protected final int[] loadVectorDimensions(RAbstractContainer vector) { + if (vector instanceof RDataFrame) { + // TODO (chumer) its unfortunate that we need this hack for the data frame dimensions + // maybe we can rid of this as soon as a data frame is of the same type as a list. + if (getDataFrameDimension == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + getDataFrameDimension = insert(new GetDataFrameDimensionNode()); + } + return getDataFrameDimension.calculateFrameDimensions((RDataFrame) vector); + } else { + return vector.getDimensions(); + } + } + + public ElementAccessMode getMode() { + return mode; + } + + protected String tryCastSingleString(PositionsCheckNode check, Object[] positions) { + if (numberOfDimensions > 1) { + return null; + } + + String positionString = null; + Object position = check.getPositionCheckAt(0).getPositionClass().cast(positions[0]); + if (position instanceof String) { + positionString = (String) position; + } else if (position instanceof RAbstractStringVector) { + RAbstractStringVector vector = (RAbstractStringVector) position; + if (vector.getLength() == 1) { + positionString = vector.getDataAt(0); + } + } + return positionString; + } + + private final class GetDataFrameDimensionNode extends Node { + + private final ConditionProfile compressedProfile = ConditionProfile.createBinaryProfile(); + private final RAttributeProfiles attrProfiles = RAttributeProfiles.create(); + private final ValueProfile rowNamesProfile = ValueProfile.createClassProfile(); + private final ConditionProfile intVecProfile = ConditionProfile.createBinaryProfile(); + private final ConditionProfile nameConditionProfile = ConditionProfile.createBinaryProfile(); + + public int[] calculateFrameDimensions(RDataFrame container) { + // this largely reproduces code from ShortRowNames + Object rowNames = container.getRowNames(attrProfiles); + if (nameConditionProfile.profile(rowNames == RNull.instance)) { + return new int[]{0, container.getLength()}; + } else { + return new int[]{Math.abs(calculateN((RAbstractVector) rowNames)), container.getLength()}; + } + } + + private int calculateN(RAbstractVector rowNames) { + RAbstractVector profiledRowNames = rowNamesProfile.profile(rowNames); + if (intVecProfile.profile(profiledRowNames.getRType() == RType.Integer && profiledRowNames.getLength() == 2)) { + RAbstractIntVector rowNamesIntVector = (RAbstractIntVector) profiledRowNames; + if (compressedProfile.profile(RRuntime.isNA(rowNamesIntVector.getDataAt(0)))) { + return rowNamesIntVector.getDataAt(1); + } else { + return profiledRowNames.getLength(); + } + } else { + return profiledRowNames.getLength(); + } + } + + } + +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAccessibleStore.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ElementAccessMode.java similarity index 66% rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAccessibleStore.java rename to com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ElementAccessMode.java index a931d56148bdf2a3dd9eb89e8141989230ba5508..e0fe53bd1131389fb151c95f530e226d54f38bca 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAccessibleStore.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ElementAccessMode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,15 +20,22 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.r.runtime.data.model; +package com.oracle.truffle.r.nodes.access.vector; -/** - * Marks a vectors internal store to be accessible. As the internal store depends on the - * implementation of the vector, users of this method are discouraged to use this interface unless - * it is really necessary. One reason might be to avoid store field lookups when traversing vectors. - */ -public interface RAccessibleStore<T> { +public enum ElementAccessMode { + + /* x[[a]] */ + SUBSCRIPT, + + /* x[a] */ + SUBSET; + + public boolean isSubset() { + return this == SUBSET; + } - T getInternalStore(); + public boolean isSubscript() { + return this == SUBSCRIPT; + } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java new file mode 100644 index 0000000000000000000000000000000000000000..3e02b18887e815a8a30f35a96ec5c5493cc804d0 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractVectorNode.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.r.nodes.binary.*; +import com.oracle.truffle.r.nodes.profile.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +public abstract class ExtractVectorNode extends Node { + + /* + * TODO remove this as soon as the new vector access nodes are the default. + */ + public static final boolean USE_NODE = ReplaceVectorNode.USE_NODE; + + protected static final int CACHE_LIMIT = 5; + + protected final ElementAccessMode mode; + private final boolean recursive; + + @Child private BoxPrimitiveNode boxVector = BoxPrimitiveNode.create(); + @Child private BoxPrimitiveNode boxExact = BoxPrimitiveNode.create(); + @Child private BoxPrimitiveNode boxDropdimensions = BoxPrimitiveNode.create(); + + ExtractVectorNode(ElementAccessMode mode, boolean recursive) { + this.mode = mode; + this.recursive = recursive; + } + + public ElementAccessMode getMode() { + return mode; + } + + public final Object apply(Object vector, Object[] positions, Object exact, Object dropDimensions) { + return execute(boxVector.execute(vector), positions, boxExact.execute(exact), boxDropdimensions.execute(dropDimensions)); + } + + public static ExtractVectorNode create(ElementAccessMode accessMode) { + return ExtractVectorNodeGen.create(accessMode, false); + } + + static ExtractVectorNode createRecursive(ElementAccessMode accessMode) { + return ExtractVectorNodeGen.create(accessMode, true); + } + + protected abstract Object execute(Object vector, Object[] positions, Object exact, Object dropDimensions); + + @Specialization(guards = {"cached != null", "cached.isSupported(vector, positions)"}) + protected Object doReplaceRecursive(RAbstractListVector vector, Object[] positions, Object exact, Object dropDimensions, // + @Cached("createRecursiveCache(vector, positions)") RecursiveExtractSubscriptNode cached) { + return cached.apply(vector, positions, exact, dropDimensions); + } + + protected RecursiveExtractSubscriptNode createRecursiveCache(Object vector, Object[] positions) { + if (isRecursiveSubscript(vector, positions)) { + return RecursiveExtractSubscriptNode.create((RAbstractListVector) vector, positions[0]); + } + return null; + } + + protected boolean isRecursiveSubscript(Object vector, Object[] positions) { + return !recursive && mode.isSubscript() && vector instanceof RAbstractListVector && positions.length == 1; + } + + @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions, exact, dropDimensions)"}) + protected Object doReplaceDefaultCached(Object vector, Object[] positions, Object exact, Object dropDimensions, // + @Cached("createDefaultCache(getThis(), vector, positions, exact, dropDimensions)") CachedExtractVectorNode cached) { + assert !isRecursiveSubscript(vector, positions); + return cached.apply(vector, positions, null, exact, dropDimensions); + } + + protected static CachedExtractVectorNode createDefaultCache(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) { + return new CachedExtractVectorNode(node.getMode(), (RTypedValue) vector, positions, (RTypedValue) exact, (RTypedValue) dropDimensions, node.recursive); + } + + @Specialization(contains = "doReplaceDefaultCached") + @TruffleBoundary + protected Object doReplaceDefaultGeneric(Object vector, Object[] positions, Object exact, Object dropDimensions, // + @Cached("new(createDefaultCache(getThis(), vector, positions, exact, dropDimensions))") GenericVectorExtractNode generic) { + return generic.get(this, vector, positions, exact, dropDimensions).apply(vector, positions, null, exact, dropDimensions); + } + + // TODO hack until Truffle-DSL supports this. + protected ExtractVectorNode getThis() { + return this; + } + + protected static final class GenericVectorExtractNode extends TruffleBoundaryNode { + + @Child private CachedExtractVectorNode cached; + + public GenericVectorExtractNode(CachedExtractVectorNode cachedOperation) { + this.cached = insert(cachedOperation); + } + + public CachedExtractVectorNode get(ExtractVectorNode node, Object vector, Object[] positions, Object exact, Object dropDimensions) { + CompilerAsserts.neverPartOfCompilation(); + if (!cached.isSupported(vector, positions, exact, dropDimensions)) { + cached = cached.replace(createDefaultCache(node, vector, positions, exact, dropDimensions)); + } + return cached; + } + + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java new file mode 100644 index 0000000000000000000000000000000000000000..fc1ab281272b49e6e8c3bd8d0577479ca6d85889 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCastNode.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.r.nodes.binary.*; +import com.oracle.truffle.r.nodes.unary.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.env.*; +import com.oracle.truffle.r.runtime.ops.na.*; + +@SuppressWarnings("unused") +abstract class PositionCastNode extends Node { + + private final ElementAccessMode mode; + private final boolean replace; + + PositionCastNode(ElementAccessMode mode, boolean replace) { + this.mode = mode; + this.replace = replace; + } + + public static PositionCastNode create(ElementAccessMode mode, boolean replace) { + return PositionCastNodeGen.create(mode, replace); + } + + public abstract RTypedValue execute(Object position); + + // TODO these boxing specializations can be removed as we enabled boxing of every value. + @Specialization + protected RAbstractVector doInteger(int position) { + return RInteger.valueOf(position); + } + + @Specialization + protected RAbstractVector doString(String position) { + return RString.valueOf(position); + } + + @Specialization + protected RAbstractVector doString(RAbstractStringVector position) { + // directly supported + return position; + } + + @Specialization + protected RAbstractVector doInteger(RAbstractIntVector position) { + // directly supported + return position; + } + + @Specialization + protected RAbstractVector doFactor(RFactor position) { + return position.getVector(); + } + + @Specialization + protected RAbstractVector doDouble(double position, @Cached("create()") NACheck check) { + if (mode.isSubscript()) { + check.enable(position); + return RInteger.valueOf(check.convertDoubleToInt(position)); + } else { + return RDouble.valueOf(position); + } + } + + @Specialization + protected RAbstractVector doDouble(RAbstractDoubleVector position, // + @Cached("createIntegerCast()") CastIntegerNode cast, // + @Cached("create()") BoxPrimitiveNode box) { + if (mode.isSubscript()) { + // double gets casted to integer for subscript + return (RAbstractVector) box.execute(cast.execute(position)); + } else { + // because we need to perform a special bounds check with doubles + // we cannot yet convert the double array to int for subsets + return (RAbstractVector) box.execute(position); + } + } + + protected CastIntegerNode createIntegerCast() { + return CastIntegerNodeGen.create(true, false, false); + } + + @Specialization + protected RAbstractVector doLogical(byte position) { + // directly supported + return RLogical.valueOf(position); + } + + @Specialization + protected RAbstractVector doLogical(RAbstractLogicalVector position) { + // directly supported + return position; + } + + @Specialization + protected RMissing doSymbol(RSymbol position) { + if (position.getName().length() == 0) { + return doMissing(RMissing.instance); + } else { + throw RError.error(this, RError.Message.INVALID_SUBSCRIPT_TYPE, "symbol"); + } + } + + @Specialization + protected RMissing doMissing(RMissing position) { + if (mode.isSubscript()) { + if (replace) { + throw RError.error(this, RError.Message.MISSING_SUBSCRIPT); + } else { + throw RError.error(this, RError.Message.INVALID_SUBSCRIPT_TYPE, "symbol"); + } + } else { + return RMissing.instance; + } + } + + @Specialization + protected RMissing doEmpty(REmpty position) { + return doMissing(null); + } + + @Specialization + protected RAbstractVector doNull(RNull position) { + // NULL expands to integer(0). + return RDataFactory.createEmptyIntVector(); + } + + @Specialization(guards = "getInvalidType(position) != null") + protected RAbstractVector doInvalidType(Object position) { + throw RError.error(this, RError.Message.INVALID_SUBSCRIPT_TYPE, getInvalidType(position).getName()); + } + + protected static RType getInvalidType(Object positionValue) { + if (positionValue instanceof RAbstractRawVector) { + return RType.Raw; + } else if (positionValue instanceof RAbstractListVector) { + return RType.List; + } else if (positionValue instanceof RFunction) { + return RType.Closure; + } else if (positionValue instanceof REnvironment) { + return RType.Environment; + } else if (positionValue instanceof RAbstractComplexVector) { + return RType.Complex; + } + return null; + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java new file mode 100644 index 0000000000000000000000000000000000000000..b16a4a29802ffce824d89451e62aa914a982ce97 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +final class PositionCharacterLookupNode extends Node { + + private final ElementAccessMode mode; + private final RAttributeProfiles attributeProfiles = RAttributeProfiles.create(); + private final int numDimensions; + private final int dimensionIndex; + private final BranchProfile emptyProfile = BranchProfile.create(); + + @Child private SearchFirstStringNode searchNode; + + public PositionCharacterLookupNode(ElementAccessMode mode, int numDimensions, int dimensionIndex, boolean useNAForNotFound, boolean exact) { + this.numDimensions = numDimensions; + this.dimensionIndex = dimensionIndex; + this.searchNode = SearchFirstStringNode.createNode(exact, useNAForNotFound); + this.mode = mode; + } + + public RAbstractIntVector execute(RAbstractContainer target, RAbstractStringVector position, int notFoundStartIndex) { + // lookup names for single dimension case + RAbstractIntVector result; + if (numDimensions <= 1) { + RStringVector names = target.getNames(attributeProfiles); + if (names == null) { + emptyProfile.enter(); + names = RDataFactory.createEmptyStringVector(); + } + result = searchNode.apply(names, position, notFoundStartIndex); + result.setNames(position.materialize()); + } else { + RList dimNames = target.getDimNames(attributeProfiles); + if (dimNames != null) { + Object dataAt = dimNames.getDataAt(dimensionIndex); + if (dataAt != RNull.instance) { + RStringVector dimName = (RStringVector) dataAt; + result = searchNode.apply(dimName, position, notFoundStartIndex); + } else { + emptyProfile.enter(); + throw noDimNames(); + } + } else { + emptyProfile.enter(); + throw noDimNames(); + } + + } + return result; + } + + private RError noDimNames() { + if (mode.isSubset()) { + return RError.error(this, Message.NO_ARRAY_DIMNAMES); + } else { + return RError.error(this, Message.SUBSCRIPT_BOUNDS); + } + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java new file mode 100644 index 0000000000000000000000000000000000000000..4e4a3872305a7938723b527a11b67b8d69b371f8 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile; +import com.oracle.truffle.r.nodes.control.*; +import com.oracle.truffle.r.nodes.profile.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +abstract class PositionCheckNode extends Node { + + protected final Class<?> positionClass; + protected final int dimensionIndex; + protected final int numDimensions; + protected final VectorLengthProfile positionLengthProfile = VectorLengthProfile.create(); + protected final BranchProfile error = BranchProfile.create(); + protected final boolean replace; + protected final RType containerType; + @Child private PositionCastNode castNode; + @Child private RLengthNode positionLengthNode = RLengthNode.create(); + @Child private PositionCharacterLookupNode characterLookup; + + PositionCheckNode(ElementAccessMode mode, RType containerType, Object positionValue, int dimensionIndex, int numDimensions, boolean exact, boolean replace) { + this.positionClass = positionValue.getClass(); + this.dimensionIndex = dimensionIndex; + this.numDimensions = numDimensions; + this.replace = replace; + this.containerType = containerType; + this.castNode = PositionCastNode.create(mode, replace); + if (positionValue instanceof String || positionValue instanceof RAbstractStringVector) { + boolean useNAForNotFound = !replace && containerType == RType.List && mode.isSubscript(); + characterLookup = new PositionCharacterLookupNode(mode, numDimensions, dimensionIndex, useNAForNotFound, exact); + } + } + + public boolean isIgnoreDimension() { + return positionClass == RMissing.class; + } + + public Class<?> getPositionClass() { + return positionClass; + } + + public final boolean isSupported(Object object) { + return object.getClass() == positionClass; + } + + public static PositionCheckNode createNode(ElementAccessMode mode, RType containerType, Object position, int positionIndex, int numDimensions, boolean exact, boolean replace, boolean recursive) { + if (mode.isSubset()) { + return PositionCheckSubsetNodeGen.create(mode, containerType, position, positionIndex, numDimensions, exact, replace); + } else { + return PositionCheckSubscriptNodeGen.create(mode, containerType, position, positionIndex, numDimensions, exact, replace, recursive); + } + } + + protected boolean isMultiDimension() { + return numDimensions > 1; + } + + public final Object execute(PositionProfile profile, RAbstractContainer vector, int[] vectorDimensions, int vectorLength, Object position) { + Object castPosition = castNode.execute(positionClass.cast(position)); + + int dimensionLength; + if (numDimensions == 1) { + dimensionLength = vectorLength; + } else { + assert vectorDimensions != null; + assert vectorDimensions.length == numDimensions; + dimensionLength = vectorDimensions[dimensionIndex]; + } + + if (characterLookup != null) { + castPosition = characterLookup.execute(vector, (RAbstractStringVector) castPosition, dimensionLength); + } + + RTypedValue positionVector = (RTypedValue) profilePosition(castPosition); + + int positionLength; + if (positionVector instanceof RMissing) { + positionLength = -1; + } else { + positionLength = positionLengthProfile.profile(((RAbstractVector) positionVector).getLength()); + } + + assert isValidCastedType(positionVector) : "result type of a position cast node must be integer or logical"; + + return execute(profile, dimensionLength, positionVector, positionLength); + } + + private final ValueProfile castedValue = ValueProfile.createClassProfile(); + + Object profilePosition(Object positionVector) { + return castedValue.profile(positionVector); + } + + private static boolean isValidCastedType(RTypedValue positionVector) { + RType type = positionVector.getRType(); + return type == RType.Integer || type == RType.Logical || type == RType.Character || type == RType.Double || type == RType.Null; + } + + public abstract Object execute(PositionProfile statistics, int dimensionLength, Object position, int positionLength); + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java new file mode 100644 index 0000000000000000000000000000000000000000..43643d26027d9205dd0b55bb235d82aab26ce710 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.ops.na.*; + +abstract class PositionCheckSubscriptNode extends PositionCheckNode { + + private final NACheck positionNACheck = NACheck.create(); + private final ConditionProfile greaterZero = ConditionProfile.createBinaryProfile(); + + private final boolean recursive; + + private final RAttributeProfiles attributeProfile = RAttributeProfiles.create(); + + PositionCheckSubscriptNode(ElementAccessMode mode, RType containerType, Object positionValue, int dimensionIndex, int numDimensions, boolean exact, boolean assignment, boolean recursive) { + super(mode, containerType, positionValue, dimensionIndex, numDimensions, exact, assignment); + this.recursive = recursive; + } + + @SuppressWarnings("unused") + @Specialization + protected Object doMissing(PositionProfile statistics, int dimSize, RMissing position, int positionLength) { + statistics.selectedPositionsCount = dimSize; + return position; + } + + @Specialization + protected RAbstractVector doLogical(PositionProfile statistics, int dimSize, RAbstractLogicalVector position, int positionLength) { + positionNACheck.enable(position); + byte value = position.getDataAt(0); + if (positionLength != 1) { + error.enter(); + if (positionLength >= 3) { + throw RError.error(this, RError.Message.SELECT_MORE_1); + } else { + if (value == RRuntime.LOGICAL_TRUE) { + throw RError.error(this, RError.Message.SELECT_MORE_1); + } else { + throw RError.error(this, RError.Message.SELECT_LESS_1); + } + } + } + + return doIntegerImpl(statistics, dimSize, positionNACheck.convertLogicalToInt(value), position); + } + + @Specialization + protected RAbstractVector doInteger(PositionProfile profile, int dimSize, RAbstractIntVector position, int positionLength) { + if (positionLength != 1) { + error.enter(); + if (positionLength > 1) { + throw RError.error(this, RError.Message.SELECT_MORE_1); + } else { + throw RError.error(this, RError.Message.SELECT_LESS_1); + } + } + assert positionLength == 1; + positionNACheck.enable(position); + return doIntegerImpl(profile, dimSize, position.getDataAt(0), position); + } + + private RAbstractVector doIntegerImpl(PositionProfile profile, int dimSize, int value, RAbstractVector originalVector) { + int result; + if (greaterZero.profile(value > 0)) { + // fast path + assert RRuntime.INT_NA <= 0; + result = value; + if (!replace && result > dimSize) { + error.enter(); + throwBoundsError(); + } + profile.maxOutOfBoundsIndex = result; + } else { + // slow path + result = doIntegerSlowPath(profile, dimSize, value); + } + profile.selectedPositionsCount = 1; + + RStringVector names = originalVector.getNames(attributeProfile); + if (names != null) { + return RDataFactory.createIntVector(new int[]{result}, !profile.containsNA, names); + } else { + return RInteger.valueOf(result); + } + } + + private int doIntegerSlowPath(PositionProfile profile, int dimSize, int value) { + positionNACheck.enable(value); + if (positionNACheck.check(value)) { + handleNA(dimSize); + profile.containsNA = true; + return value; + } else { + if (dimSize == 2) { + if (value == -2) { + return 1; + } else if (value == -1) { + return 2; + } + } + error.enter(); + if (value == 0 || dimSize <= 2) { + throw RError.error(this, RError.Message.SELECT_LESS_1); + } else { + throw RError.error(this, RError.Message.SELECT_MORE_1); + } + } + } + + private void handleNA(int dimSize) { + if (replace) { + error.enter(); + Message message; + if (isMultiDimension()) { + message = RError.Message.SUBSCRIPT_BOUNDS_SUB; + } else { + if (dimSize < 2) { + message = RError.Message.SELECT_LESS_1; + } else { + message = RError.Message.SELECT_MORE_1; + } + } + throw RError.error(this, message); + } else { + if (containerType == RType.List && numDimensions == 1) { + // lists pass on the NA value + } else { + error.enter(); + throwBoundsError(); + } + } + } + + private void throwBoundsError() { + if (recursive) { + throw new RecursiveIndexNotFoundError(); + } else { + throw RError.error(this, RError.Message.SUBSCRIPT_BOUNDS); + } + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java new file mode 100644 index 0000000000000000000000000000000000000000..39be205e5cd8d3303bd89f696410c6b42289882d --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import java.util.*; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.ops.na.*; + +abstract class PositionCheckSubsetNode extends PositionCheckNode { + + private final NACheck positionNACheck = NACheck.create(); + + PositionCheckSubsetNode(ElementAccessMode mode, RType containerType, Object positionValue, int dimensionIndex, int numDimensions, boolean exact, boolean assignment) { + super(mode, containerType, positionValue, dimensionIndex, numDimensions, exact, assignment); + } + + @SuppressWarnings("unused") + @Specialization + protected Object doMissing(PositionProfile statistics, int dimSize, RMissing position, int positionLength) { + statistics.selectedPositionsCount = dimSize; + return position; + } + + @Specialization(guards = {"isMultiplesOf(dimensionLength, positionLength)", "positionLength <= dimensionLength"}) + protected RAbstractVector doLogicalMultiplesInBounds(PositionProfile statistics, int dimensionLength, RAbstractLogicalVector position, int positionLength) { + assert positionLength > 0; + positionNACheck.enable(position); + int elementCount = 0; + boolean hasSeenNA = false; + for (int i = 0; i < positionLength; i++) { + byte positionValue = position.getDataAt(i); + if (positionNACheck.check(positionValue)) { + hasSeenNA = true; + elementCount++; + } + if (positionValue == RRuntime.LOGICAL_TRUE) { + elementCount++; + } + } + statistics.containsNA = hasSeenNA; + statistics.maxOutOfBoundsIndex = positionLength; + statistics.selectedPositionsCount = elementCount * (dimensionLength / positionLength); + return position; + } + + protected static boolean isMultiplesOf(int a, int b) { + return b != 0 && a % b == 0; + } + + @Specialization(contains = "doLogicalMultiplesInBounds") + protected RAbstractVector doLogicalGenericInBounds(PositionProfile statistics, // + int dimensionLength, RAbstractLogicalVector position, int positionLength, @Cached("create()") BranchProfile outOfBoundsProfile) { + positionNACheck.enable(position); + int positionIndex = 0; + int elementCount = 0; + boolean hasSeenNA = false; + if (positionLength > 0) { + int length = dimensionLength; + if (length < positionLength) { + outOfBoundsProfile.enter(); + if (isMultiDimension()) { + error.enter(); + throw RError.error(this, RError.Message.LOGICAL_SUBSCRIPT_LONG); + } + length = positionLength; + } + for (int i = 0; i < length; i++) { + byte positionValue = position.getDataAt(positionIndex); + // boolean outOfBounds = outOfBoundsProfile.isVisited() && i >= dimensionLength; + if (positionNACheck.check(positionValue)) { + hasSeenNA = true; + elementCount++; + } + if (positionValue == RRuntime.LOGICAL_TRUE) { + elementCount++; + } + positionIndex = Utils.incMod(positionIndex, positionLength); + } + } + statistics.containsNA = hasSeenNA; + statistics.maxOutOfBoundsIndex = positionLength; + statistics.selectedPositionsCount = elementCount; + return position; + } + + private final RAttributeProfiles attrProfiles = RAttributeProfiles.create(); + + @Specialization(/* contains = "doSequence" */) + protected RAbstractVector doDouble(PositionProfile profile, int dimensionLength, RAbstractDoubleVector position, int positionLength, // + @Cached("create()") BranchProfile seenZeroProfile, // + @Cached("create()") BranchProfile seenPositiveProfile, // + @Cached("create()") BranchProfile seenNegativeProfile, // + @Cached("create()") BranchProfile seenOutOfBounds, // + @Cached("create()") NullProfile hasNamesProfile) { + RAbstractIntVector intPosition = RDataFactory.createIntVector(positionLength); + // requires names preservation + RStringVector names = hasNamesProfile.profile(position.getNames(attrProfiles)); + if (names != null) { + intPosition.setNames(names); + } + Object convertedStore = intPosition.getInternalStore(); + + positionNACheck.enable(position); + boolean hasSeenPositive = false; + boolean hasSeenNegative = false; + boolean hasSeenNA = false; + int outOfBoundsCount = 0; + int zeroCount = 0; + int maxOutOfBoundsIndex = 0; + for (int i = 0; i < positionLength; i++) { + double positionValue = position.getDataAt(i); + if (positionValue >= 1) { + seenPositiveProfile.enter(); + hasSeenPositive = true; + int intPositionValue = RRuntime.double2intNoCheck(positionValue); + if (positionValue > dimensionLength) { + seenOutOfBounds.enter(); + outOfBoundsCount++; + maxOutOfBoundsIndex = Math.max(maxOutOfBoundsIndex, intPositionValue); + } + intPosition.setDataAt(convertedStore, i, intPositionValue); + } else if (positionValue >= 0 && positionValue < 1) { + seenZeroProfile.enter(); + zeroCount++; + } else if (positionNACheck.checkNAorNaN(positionValue)) { + hasSeenNA = true; + intPosition.setNA(convertedStore, i); + } else { + seenNegativeProfile.enter(); + assert positionValue < 0; + hasSeenNegative = true; + int intPositionValue = RRuntime.double2intNoCheck(positionValue); + if (intPositionValue == 0) { + /* + * It seems that the range ]-2:0[ is all translated to -1. So much for + * continuous math properties. + */ + intPositionValue = -1; + } + if (-positionValue > dimensionLength) { + seenOutOfBounds.enter(); + outOfBoundsCount++; + /* + * We need to decrement the value to ensure that the later nodes see that the + * value is actually out of bounds. + */ + intPositionValue--; + } + intPosition.setDataAt(convertedStore, i, intPositionValue); + } + } + + return doIntegerProfiled(profile, dimensionLength, intPosition, positionLength, hasSeenPositive, hasSeenNegative, hasSeenNA, outOfBoundsCount, zeroCount, maxOutOfBoundsIndex); + + } + + @Specialization(/* contains = "doSequence" */) + protected RAbstractVector doInteger(PositionProfile profile, int dimensionLength, RAbstractIntVector position, int positionLength, // + @Cached("create()") BranchProfile seenZeroProfile, // + @Cached("create()") BranchProfile seenPositiveProfile, // + @Cached("create()") BranchProfile seenNegativeProfile, // + @Cached("create()") BranchProfile seenOutOfBounds) { + + positionNACheck.enable(position); + boolean hasSeenPositive = false; + boolean hasSeenNegative = false; + boolean hasSeenNA = false; + int outOfBoundsCount = 0; + int zeroCount = 0; + int maxOutOfBoundsIndex = 0; + for (int i = 0; i < positionLength; i++) { + int positionValue = position.getDataAt(i); + if (positionValue > 0) { + seenPositiveProfile.enter(); + hasSeenPositive = true; + if (positionValue > dimensionLength) { + seenOutOfBounds.enter(); + outOfBoundsCount++; + maxOutOfBoundsIndex = Math.max(maxOutOfBoundsIndex, positionValue); + } + } else if (positionValue == 0) { + seenZeroProfile.enter(); + zeroCount++; + } else if (positionNACheck.check(positionValue)) { + hasSeenNA = true; + } else { + assert positionValue != RRuntime.INT_NA; + seenNegativeProfile.enter(); + assert positionValue < 0; + hasSeenNegative = true; + if (-positionValue > dimensionLength) { + seenOutOfBounds.enter(); + outOfBoundsCount++; + } + } + } + + return doIntegerProfiled(profile, dimensionLength, position, positionLength, hasSeenPositive, hasSeenNegative, hasSeenNA, outOfBoundsCount, zeroCount, maxOutOfBoundsIndex); + } + + private RAbstractVector doIntegerProfiled(PositionProfile profile, int dimensionLength, RAbstractIntVector intPosition, int positionLength, boolean hasSeenPositive, boolean hasSeenNegative, + boolean hasSeenNA, int outOfBoundsCount, int zeroCount, int maxOutOfBoundsIndex) { + if (hasSeenPositive || hasSeenNA) { + if (numDimensions > 1 && outOfBoundsCount > 0) { + error.enter(); + throw RError.error(this, RError.Message.SUBSCRIPT_BOUNDS); + } + if (hasSeenNegative) { + error.enter(); + throw RError.error(this, RError.Message.ONLY_0_MIXED); + } + profile.maxOutOfBoundsIndex = maxOutOfBoundsIndex; + profile.selectedPositionsCount = positionLength - zeroCount; + boolean containsNAForOutOfBounds = !replace && outOfBoundsCount > 0; + profile.containsNA = hasSeenNA || containsNAForOutOfBounds; + + if (zeroCount == 0 && !containsNAForOutOfBounds) { + // fast path (most common expected behavior) + return intPosition; + } else { + return eliminateZerosAndOutOfBounds(intPosition, positionLength, dimensionLength, outOfBoundsCount, zeroCount, hasSeenNA); + } + } else if (hasSeenNegative) { + assert !hasSeenNA; + return transformNegative(profile, dimensionLength, intPosition, positionLength, zeroCount > 0); + } else { + return RDataFactory.createEmptyIntVector(); + } + } + + private RAbstractVector eliminateZerosAndOutOfBounds(RAbstractIntVector position, int positionLength, int dimensionLength, int outOfBoundsCount, int zeroCount, boolean hasSeenNA) { + int[] newIndices = new int[positionLength - zeroCount]; + int newPositionIndex = 0; + for (int i = 0; i < positionLength; i++) { + int positionValue = position.getDataAt(i); + if (zeroCount > 0 && positionValue == 0) { + continue; + } else if (!replace && outOfBoundsCount > 0 && positionValue > dimensionLength) { + newIndices[newPositionIndex++] = RRuntime.INT_NA; + } else { + newIndices[newPositionIndex++] = positionValue; + } + } + return RDataFactory.createIntVector(newIndices, !hasSeenNA && outOfBoundsCount == 0); + } + + private static RAbstractVector transformNegative(PositionProfile statistics, int dimLength, RAbstractIntVector position, int positionLength, boolean hasZeros) { + byte[] mask = new byte[dimLength]; + Arrays.fill(mask, RRuntime.LOGICAL_TRUE); + int allPositionsNum = dimLength; + for (int i = 0; i < positionLength; i++) { + int pos = -position.getDataAt(i); + if (hasZeros && pos == 0) { + continue; + } + assert pos > 0; + if (pos <= dimLength && mask[pos - 1] != RRuntime.LOGICAL_FALSE) { + allPositionsNum--; + mask[pos - 1] = RRuntime.LOGICAL_FALSE; + } + } + statistics.selectedPositionsCount = allPositionsNum; + return RDataFactory.createLogicalVector(mask, RDataFactory.COMPLETE_VECTOR); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java new file mode 100644 index 0000000000000000000000000000000000000000..ab3c7c1e2302dd30bbdac22cf18446c57922cd77 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.profile.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +final class PositionsCheckNode extends Node { + + @Children private final PositionCheckNode[] positionsCheck; + + private final ElementAccessMode mode; + private final BranchProfile errorBranch = BranchProfile.create(); + private final VectorLengthProfile selectedPositionsCountProfile = VectorLengthProfile.create(); + private final VectorLengthProfile maxOutOfBoundsProfile = VectorLengthProfile.create(); + private final ConditionProfile containsNAProfile = ConditionProfile.createBinaryProfile(); + private final boolean replace; + private final int positionsLength; + + public PositionsCheckNode(ElementAccessMode mode, RType containerType, Object[] positions, boolean exact, boolean replace, boolean recursive) { + this.mode = mode; + this.replace = replace; + this.positionsCheck = new PositionCheckNode[positions.length]; + for (int i = 0; i < positions.length; i++) { + positionsCheck[i] = PositionCheckNode.createNode(mode, containerType, positions[i], i, positions.length, exact, replace, recursive); + } + this.positionsLength = positions.length; + } + + public PositionCheckNode getPositionCheckAt(int index) { + return positionsCheck[index]; + } + + @ExplodeLoop + public boolean isSupported(Object[] positions) { + if (positionsCheck.length != positions.length) { + return false; + } + for (int i = 0; i < positionsCheck.length; i++) { + if (!positionsCheck[i].isSupported(positions[i])) { + return false; + } + } + return true; + } + + public int getDimensions() { + return positionsCheck.length; + } + + public boolean isSingleDimension() { + return positionsCheck.length == 1; + } + + public boolean isMultiDimension() { + return positionsCheck.length > 1; + } + + @ExplodeLoop + public PositionProfile[] executeCheck(RAbstractContainer vector, int[] vectorDimensions, int vectorLength, Object[] positions) { + assert isSupported(positions); + verifyDimensions(vectorDimensions); + + PositionProfile[] statistics = new PositionProfile[positionsLength]; + for (int i = 0; i < positionsLength; i++) { + Object position = positions[i]; + PositionProfile profile = new PositionProfile(); + positions[i] = positionsCheck[i].execute(profile, vector, vectorDimensions, vectorLength, position); + statistics[i] = profile; + } + return statistics; + } + + @TruffleBoundary + private void print() { + System.out.println(positionsCheck.length); + } + + private void verifyDimensions(int[] vectorDimensions) { + if (vectorDimensions == null) { + if (isMultiDimension()) { + errorBranch.enter(); + throw dimensionsError(); + } + } else { + if (getDimensions() > vectorDimensions.length || getDimensions() < vectorDimensions.length) { + errorBranch.enter(); + throw dimensionsError(); + } + } + } + + private RError dimensionsError() { + if (replace) { + if (mode.isSubset()) { + if (getDimensions() == 2) { + return RError.error(this, RError.Message.INCORRECT_SUBSCRIPTS_MATRIX); + } else { + return RError.error(this, RError.Message.INCORRECT_SUBSCRIPTS); + } + } else { + return RError.error(this, RError.Message.IMPROPER_SUBSCRIPT); + } + } else { + return RError.error(this, RError.Message.INCORRECT_DIMENSIONS); + } + } + + @ExplodeLoop + public int getSelectedPositionsCount(PositionProfile[] profiles) { + if (positionsCheck.length == 1) { + return selectedPositionsCountProfile.profile(profiles[0].selectedPositionsCount); + } else { + int newSize = 1; + for (int i = 0; i < positionsCheck.length; i++) { + newSize *= profiles[i].selectedPositionsCount; + } + return selectedPositionsCountProfile.profile(newSize); + } + } + + public int getCachedSelectedPositionsCount() { + return selectedPositionsCountProfile.getCachedLength(); + } + + @ExplodeLoop + public boolean getContainsNA(PositionProfile[] profiles) { + if (positionsCheck.length == 1) { + return containsNAProfile.profile(profiles[0].containsNA); + } else { + boolean containsNA = false; + for (int i = 0; i < positionsCheck.length; i++) { + containsNA |= profiles[i].containsNA; + } + return containsNAProfile.profile(containsNA); + } + } + + @ExplodeLoop + public int getMaxOutOfBounds(PositionProfile[] replacementStatistics) { + if (positionsCheck.length == 1) { + return maxOutOfBoundsProfile.profile(replacementStatistics[0].maxOutOfBoundsIndex); + } else { + // impossible to be relevant as position check will throw an error in this case. + return 0; + } + } + + public boolean isMissing() { + return positionsCheck.length == 1 && // + (positionsCheck[0].getPositionClass() == RMissing.class || // + positionsCheck[0].getPositionClass() == RSymbol.class); + } + + final class PositionProfile { + + int selectedPositionsCount; + + int maxOutOfBoundsIndex; + + boolean containsNA; + + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveExtractSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveExtractSubscriptNode.java new file mode 100644 index 0000000000000000000000000000000000000000..1300b80b543be6d8156d3abca1c8ae52c47dca07 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveExtractSubscriptNode.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +abstract class RecursiveExtractSubscriptNode extends RecursiveSubscriptNode { + + @Child private ExtractVectorNode recursiveSubscriptExtract = ExtractVectorNode.createRecursive(ElementAccessMode.SUBSCRIPT); + @Child private ExtractVectorNode getPositionExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT); + + public RecursiveExtractSubscriptNode(RAbstractListVector vector, Object position) { + super(vector, position); + } + + public static RecursiveExtractSubscriptNode create(RAbstractListVector vector, Object position) { + return RecursiveExtractSubscriptNodeGen.create(vector, position); + } + + public final Object apply(Object vector, Object[] positions, Object exact, Object dropDimensions) { + Object firstPosition = positions[0]; + int length = positionLengthNode.executeInteger(firstPosition); + return execute(vector, positions, firstPosition, length, exact, dropDimensions); + } + + protected abstract Object execute(Object vector, Object[] positions, Object firstPosition, int positionLength, Object exact, Object dropDimensions); + + @Specialization(guards = "positionLength <= 1") + @SuppressWarnings("unused") + protected Object doDefault(Object vector, Object[] positions, Object firstPosition, int positionLength, Object exact, Object dropDimensions) { + try { + return recursiveSubscriptExtract.apply(vector, positions, exact, dropDimensions); + } catch (RecursiveIndexNotFoundError e) { + errorBranch.enter(); + throw RError.error(this, RError.Message.SUBSCRIPT_BOUNDS); + } + } + + @Specialization(contains = "doDefault") + @SuppressWarnings("unused") + protected Object doRecursive(Object vector, Object[] positions, Object originalFirstPosition, int positionLength, Object exact, Object dropDimensions, // + @Cached("createPositionCast()") PositionCastNode positionCast) { + Object firstPosition = positionCast.execute(originalFirstPosition); + Object currentVector = vector; + for (int i = 1; i <= positionLength; i++) { + Object selection = getPositionExtract.apply(firstPosition, new Object[]{RInteger.valueOf(i)}, RLogical.TRUE, RLogical.TRUE); + try { + if (i < positionLength && !(currentVector instanceof RAbstractListVector)) { + if (currentVector == RNull.instance) { + throw noSuchIndex(i - 1); + } else { + throw indexingFailed(i - 1); + } + } else if (i <= positionLength && currentVector == RNull.instance) { + throw noSuchIndex(i - 1); + } + currentVector = recursiveSubscriptExtract.apply(currentVector, new Object[]{selection}, exact, dropDimensions); + + } catch (RecursiveIndexNotFoundError e) { + errorBranch.enter(); + if (i > 1) { + throw noSuchIndex(i - 1); + } else { + throw RError.error(this, RError.Message.SUBSCRIPT_BOUNDS); + } + } + } + return currentVector; + } + + protected PositionCastNode createPositionCast() { + return PositionCastNode.create(ElementAccessMode.SUBSCRIPT, true); + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/GlobalAssumptions.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveIndexNotFoundError.java similarity index 65% rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/GlobalAssumptions.java rename to com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveIndexNotFoundError.java index dc0c079a0b5c2257ec062969bd406c4894f846b8..6cf1a25c4e42106aaed77cafcef9fab656557139 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/GlobalAssumptions.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveIndexNotFoundError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -20,17 +20,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.r.runtime; +package com.oracle.truffle.r.nodes.access.vector; -import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; -public final class GlobalAssumptions { +final class RecursiveIndexNotFoundError extends ControlFlowException { - GlobalAssumptions() { - } - - public final Assumption naIntroduced = Truffle.getRuntime().createAssumption("naIntroduced"); - public final Assumption outOfRange = Truffle.getRuntime().createAssumption("outOfRange"); - public final Assumption imagDiscarded = Truffle.getRuntime().createAssumption("imagDiscarded"); + private static final long serialVersionUID = 1L; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveReplaceSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveReplaceSubscriptNode.java new file mode 100644 index 0000000000000000000000000000000000000000..7a48b04431d51579d0ac896db77da6e59a4e14ee --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveReplaceSubscriptNode.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +abstract class RecursiveReplaceSubscriptNode extends RecursiveSubscriptNode { + + @Child private ReplaceVectorNode recursiveSubscriptReplace = ReplaceVectorNode.createRecursive(ElementAccessMode.SUBSCRIPT); + @Child private ExtractVectorNode getPositionExtract = ExtractVectorNode.createRecursive(ElementAccessMode.SUBSCRIPT); + @Child private ExtractVectorNode recursiveSubscriptExtract = ExtractVectorNode.createRecursive(ElementAccessMode.SUBSCRIPT); + + public RecursiveReplaceSubscriptNode(RAbstractListVector vector, Object position) { + super(vector, position); + } + + public static RecursiveReplaceSubscriptNode create(RAbstractListVector vector, Object position) { + return RecursiveReplaceSubscriptNodeGen.create(vector, position); + } + + public final Object apply(Object vector, Object[] positions, Object value) { + assert isSupported(vector, positions); + Object firstPosition = positionClass.cast(positions[0]); + int length = positionLengthNode.executeInteger(firstPosition); + return execute(vectorClass.cast(vector), positions, firstPosition, length, value); + } + + protected abstract Object execute(Object vector, Object[] positions, Object firstPosition, int positionLength, Object value); + + @Specialization(guards = "positionLength <= 1") + @SuppressWarnings("unused") + protected Object doDefault(Object vector, Object[] positions, Object firstPosition, int positionLength, Object value) { + return recursiveSubscriptReplace.apply(vector, positions, value); + } + + /** + * Exemplary expansion. <code> + * a <- list(1,list(1,list(1))); a[[c(2,2,2)]] <- 2 + * Gets desugared into: + * tmp1 <- a[[2]] + * tmp2 <- tmp1[[2]] + * tmp2[[2]] <- 2 + * tmp1[[2]] <- tmp2 + * a[[2]] <- tmp1 + * </code> + */ + @Specialization(contains = "doDefault") + @SuppressWarnings("unused") + protected Object doRecursive(Object vector, Object[] positions, Object originalFirstPosition, int positionLength, Object value, // + @Cached("createPositionCast()") PositionCastNode positionCast) { + Object firstPosition = positionCast.execute(originalFirstPosition); + Object[] positionStack = new Object[positionLength]; + Object[] valueStack = new Object[positionLength]; + valueStack[0] = vector; + Object currentVector = vector; + for (int i = 1; i < positionLength; i++) { + Object parentPosition = getPositionValue(firstPosition, i - 1); + positionStack[i - 1] = parentPosition; + try { + if (!(currentVector instanceof RAbstractListVector)) { + throw indexingFailed(i); + } + currentVector = recursiveSubscriptExtract.apply(currentVector, new Object[]{parentPosition}, RLogical.TRUE, RLogical.TRUE); + + if (currentVector == RNull.instance) { + throw noSuchIndex(i); + } + valueStack[i] = currentVector; + } catch (RecursiveIndexNotFoundError e) { + throw noSuchIndex(i); + } + } + Object recursiveValue = value; + positionStack[positionLength - 1] = getPositionValue(firstPosition, positionLength - 1); + for (int i = positionLength - 1; i >= 0; i--) { + recursiveValue = recursiveSubscriptReplace.apply(valueStack[i], new Object[]{positionStack[i]}, recursiveValue); + } + return recursiveValue; + } + + protected PositionCastNode createPositionCast() { + return PositionCastNode.create(ElementAccessMode.SUBSCRIPT, false); + } + + private Object getPositionValue(Object firstPosition, int i) { + return getPositionExtract.apply(firstPosition, new Object[]{RInteger.valueOf(i + 1)}, RLogical.TRUE, RLogical.TRUE); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveSubscriptNode.java new file mode 100644 index 0000000000000000000000000000000000000000..abdf54b5dd789feea8694c0c696e7a3ef928dd09 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/RecursiveSubscriptNode.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.control.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.model.*; + +abstract class RecursiveSubscriptNode extends Node { + + protected final Class<?> vectorClass; + protected final Class<?> positionClass; + + protected final BranchProfile errorBranch = BranchProfile.create(); + + @Child protected RLengthNode positionLengthNode = RLengthNode.create(); + + public RecursiveSubscriptNode(RAbstractListVector vector, Object position) { + this.vectorClass = vector.getClass(); + this.positionClass = position.getClass(); + } + + public final boolean isSupported(Object vector, Object[] positions) { + if (vector.getClass() == vectorClass && positions.length == 1 && positions[0].getClass() == positionClass) { + return true; + } + return false; + } + + protected final RError indexingFailed(int i) { + errorBranch.enter(); + return RError.error(this, RError.Message.RECURSIVE_INDEXING_FAILED, i); + } + + protected final RError noSuchIndex(int i) { + errorBranch.enter(); + return RError.error(this, RError.Message.NO_SUCH_INDEX, i); + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java new file mode 100644 index 0000000000000000000000000000000000000000..de68da1418eaa0913f786321bc641ce428d9eff6 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ReplaceVectorNode.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.r.nodes.binary.*; +import com.oracle.truffle.r.nodes.profile.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.env.*; + +/** + * Syntax node for element writes. + */ +public abstract class ReplaceVectorNode extends Node { + + /* + * TODO remove this as soon as the new vector access nodes are the default. + */ + public static final boolean USE_NODE = System.getProperty("newVectorAccess") != null ? System.getProperty("newVectorAccess").equals("true") : false; + + protected static final int CACHE_LIMIT = 5; + + private final ElementAccessMode mode; + private final boolean recursive; + + @Child private BoxPrimitiveNode boxVector = BoxPrimitiveNode.create(); + @Child private BoxPrimitiveNode boxValue = BoxPrimitiveNode.create(); + + public ReplaceVectorNode(ElementAccessMode mode, boolean recursive) { + this.mode = mode; + this.recursive = recursive; + } + + public final Object apply(Object vector, Object[] positions, Object value) { + return execute(boxVector.execute(vector), positions, boxValue.execute(value)); + } + + protected abstract Object execute(Object vector, Object[] positions, Object value); + + public static ReplaceVectorNode create(ElementAccessMode mode) { + return ReplaceVectorNodeGen.create(mode, false); + } + + static ReplaceVectorNode createRecursive(ElementAccessMode mode) { + return ReplaceVectorNodeGen.create(mode, true); + } + + @SuppressWarnings("unchecked") + protected Class<? extends RTypedValue> getInvalidVectorType(Object vector) { + if (vector instanceof REnvironment || vector instanceof RFunction) { + return (Class<? extends RTypedValue>) vector.getClass(); + } + return null; + } + + @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions)"}) + protected Object doRecursive(RAbstractListVector vector, Object[] positions, Object value, // + @Cached("createRecursiveCache(vector, positions)") RecursiveReplaceSubscriptNode cached) { + return cached.apply(vector, positions, value); + } + + protected RecursiveReplaceSubscriptNode createRecursiveCache(Object vector, Object[] positions) { + if (isRecursiveSubscript(vector, positions)) { + return RecursiveReplaceSubscriptNode.create((RAbstractListVector) vector, positions[0]); + } + return null; + } + + private boolean isRecursiveSubscript(Object vector, Object[] positions) { + return !recursive && mode.isSubscript() && vector instanceof RAbstractListVector && positions.length == 1; + } + + @Specialization(limit = "CACHE_LIMIT", guards = {"cached != null", "cached.isSupported(vector, positions, value)"}) + protected Object doReplaceCached(Object vector, Object[] positions, Object value, // + @Cached("createDefaultCached(getThis(), vector, positions, value)") CachedReplaceVectorNode cached) { + assert !isRecursiveSubscript(vector, positions); + return cached.apply(vector, positions, value); + } + + protected static CachedReplaceVectorNode createDefaultCached(ReplaceVectorNode node, Object vector, Object[] positions, Object value) { + return new CachedReplaceVectorNode(node.mode, (RTypedValue) vector, positions, (RTypedValue) value, true, node.recursive); + } + + public ElementAccessMode getMode() { + return mode; + } + + @Specialization(contains = "doReplaceCached") + @TruffleBoundary + protected Object doReplaceDefaultGeneric(Object vector, Object[] positions, Object value, // + @Cached("new(createDefaultCached(getThis(), vector, positions, value))") GenericVectorReplaceNode generic) { + return generic.get(this, vector, positions, value).apply(vector, positions, value); + } + + // TODO hack until Truffle-DSL supports this. + protected ReplaceVectorNode getThis() { + return this; + } + + protected static final class GenericVectorReplaceNode extends TruffleBoundaryNode { + + @Child private CachedReplaceVectorNode cached; + + public GenericVectorReplaceNode(CachedReplaceVectorNode cachedOperation) { + this.cached = insert(cachedOperation); + } + + public CachedReplaceVectorNode get(ReplaceVectorNode node, Object vector, Object[] positions, Object value) { + CompilerAsserts.neverPartOfCompilation(); + if (!cached.isSupported(vector, positions, value)) { + cached = cached.replace(createDefaultCached(node, vector, positions, value)); + } + return cached; + } + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/SearchFirstStringNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/SearchFirstStringNode.java new file mode 100644 index 0000000000000000000000000000000000000000..fb3c0f3138e6f823edaee2cb74f540184eae0590 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/SearchFirstStringNode.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.profile.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.ops.na.*; + +/** + * This node encapsulates a speculative search of the first elements in an character vector and + * returns an integer vector with their indices. + */ +final class SearchFirstStringNode extends Node { + + private static final int[] UNINTIALIZED_CACHED_INDICES = new int[0]; + + private final VectorLengthProfile targetLengthProfile = VectorLengthProfile.create(); + private final VectorLengthProfile elementsLengthProfile = VectorLengthProfile.create(); + private final ValueProfile targetClassProfile = ValueProfile.createClassProfile(); + private final ValueProfile elementsClassProfile = ValueProfile.createClassProfile(); + + @Child private CompareStringNode stringEquals = CompareStringNode.createEquals(); + @Child private CompareStringNode stringStartsWith; + @Child private CompareStringNode equalsDuplicate; + + private final NACheck elementsNACheck = NACheck.create(); + private final NACheck targetNACheck = NACheck.create(); + private final BranchProfile everFoundDuplicate = BranchProfile.create(); + private final BranchProfile seenInvalid = BranchProfile.create(); + + /** Instead of using the notFoundStartIndex we use NA. */ + private final boolean useNAForNotFound; + protected final boolean exactMatch; + + @CompilationFinal private int[] cachedIndices; + + public SearchFirstStringNode(boolean exactMatch, boolean useNAForNotFound) { + this.exactMatch = exactMatch; + this.useNAForNotFound = useNAForNotFound; + if (!exactMatch) { + stringStartsWith = CompareStringNode.createStartsWith(); + } + } + + public RAbstractIntVector apply(RAbstractStringVector target, RAbstractStringVector elements, int notFoundStartIndex) { + RAbstractStringVector targetProfiled = targetClassProfile.profile(target); + RAbstractStringVector elementsProfiled = elementsClassProfile.profile(elements); + + int targetLength = targetLengthProfile.profile(targetProfiled.getLength()); + int elementsLength = elementsLengthProfile.profile(elementsProfiled.getLength()); + + targetNACheck.enable(target); + elementsNACheck.enable(elements); + + if (cachedIndices == UNINTIALIZED_CACHED_INDICES) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + cachedIndices = searchCached(targetProfiled, targetLength, elementsProfiled, elementsLength); + } + if (cachedIndices != null) { + if (!isCacheValid(targetProfiled, targetLength, elementsProfiled, elementsLength, cachedIndices)) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + cachedIndices = null; // set to generic + } + assert sameVector(searchCached(target, targetLength, elements, elementsLength), cachedIndices); + return RDataFactory.createIntVector(cachedIndices, true); + } + + return searchGeneric(targetProfiled, targetLength, elementsProfiled, elementsLength, notFoundStartIndex, false); + } + + public static SearchFirstStringNode createNode(boolean exactMatch, boolean useNAForNotFound) { + return new SearchFirstStringNode(exactMatch, useNAForNotFound); + } + + protected int[] searchCached(RAbstractStringVector target, int targetLength, RAbstractStringVector elements, int elementsLength) { + if (exactMatch) { + return (int[]) searchGeneric(target, targetLength, elements, elementsLength, -1, true).getInternalStore(); + } + return null; + } + + protected boolean isCacheValid(RAbstractStringVector target, int targetLength, // + RAbstractStringVector elements, int elementsLength, int[] cached) { + int cachedLength = cached.length; + if (elementsLength != cachedLength) { + seenInvalid.enter(); + return false; + } + + for (int i = 0; i < cachedLength; i++) { + int cachedIndex = cached[i]; + String cachedElement = elements.getDataAt(i); + + if (elementsNACheck.check(cachedElement) || cachedElement.length() == 0) { + seenInvalid.enter(); + return false; + } + + int cachedTranslatedIndex = cachedIndex - 1; + for (int j = 0; j < cachedTranslatedIndex; j++) { + String targetString = target.getDataAt(j); + if (!targetNACheck.check(targetString) && stringEquals.executeCompare(targetString, cachedElement)) { + seenInvalid.enter(); + return false; + } + } + if (cachedTranslatedIndex < targetLength) { + String targetString = target.getDataAt(cachedTranslatedIndex); + if (!targetNACheck.check(targetString) && !stringEquals.executeCompare(targetString, cachedElement)) { + seenInvalid.enter(); + return false; + } + } else { + seenInvalid.enter(); + return false; + } + } + return true; + + } + + private static boolean sameVector(int[] a, int[] b) { + if (a == null) { + return false; + } + if (a.length != b.length) { + return false; + } + + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + + private final BranchProfile notFoundProfile = BranchProfile.create(); + + protected RAbstractIntVector searchGeneric(RAbstractStringVector target, int targetLength, RAbstractStringVector elements, int elementsLength, int notFoundStartIndex, boolean nullOnNotFound) { + int notFoundIndex = notFoundStartIndex; + int[] indices = new int[elementsLength]; + boolean resultComplete = true; + for (int i = 0; i < elementsLength; i++) { + String element = elements.getDataAt(i); + boolean isElementNA = elementsNACheck.check(element) || element.length() == 0; + if (!isElementNA) { + int index = findIndex(target, targetLength, element); + if (index >= 0) { + indices[i] = index + 1; + continue; + } + } + notFoundProfile.enter(); + if (nullOnNotFound) { + return null; + } else { + int prevDuplicateIndex = -1; + if (!isElementNA) { + prevDuplicateIndex = findFirstDuplicate(elements, element, i); + } + int nextIndex; + if (prevDuplicateIndex == -1) { + if (useNAForNotFound) { + resultComplete = false; + nextIndex = RRuntime.INT_NA; + } else { + nextIndex = ++notFoundIndex; + } + } else { + nextIndex = indices[prevDuplicateIndex]; + } + indices[i] = nextIndex; + } + } + + return RDataFactory.createIntVector(indices, resultComplete && elements.isComplete()); + } + + private int findIndex(RAbstractStringVector target, int targetLength, String element) { + int nonExactIndex = -1; + for (int j = 0; j < targetLength; j++) { + String targetValue = target.getDataAt(j); + if (!targetNACheck.check(targetValue)) { + if (stringEquals.executeCompare(targetValue, element)) { + return j; + } + if (!exactMatch) { + if (stringStartsWith.executeCompare(targetValue, element)) { + if (nonExactIndex == -1) { + nonExactIndex = j; + } else { + nonExactIndex = -2; + } + } + } + } + } + return nonExactIndex; + } + + private int findFirstDuplicate(RAbstractStringVector elements, String element, int currentIndex) { + if (equalsDuplicate == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + equalsDuplicate = insert(CompareStringNode.createEquals()); + } + + for (int j = 0; j < currentIndex; j++) { + String otherElement = elements.getDataAt(j); + if (!targetNACheck.check(otherElement) && equalsDuplicate.executeCompare(element, otherElement)) { + everFoundDuplicate.enter(); + return j; + } + } + return -1; + } + + abstract static class CompareStringNode extends Node { + + public abstract boolean executeCompare(String target, String element); + + public static CompareStringNode createEquals() { + return new StringEqualsNode(); + } + + public static CompareStringNode createStartsWith() { + return new StringStartsWithNode(); + } + + private static class StringEqualsNode extends CompareStringNode { + + private final ConditionProfile identityEquals = ConditionProfile.createBinaryProfile(); + + @Override + public final boolean executeCompare(String a, String b) { + assert a != RRuntime.STRING_NA; + assert b != RRuntime.STRING_NA; + if (identityEquals.profile(a == b)) { + return true; + } else { + return a.equals(b); + } + } + } + + private static class StringStartsWithNode extends CompareStringNode { + + private final ConditionProfile identityEquals = ConditionProfile.createBinaryProfile(); + + @Override + public final boolean executeCompare(String a, String b) { + assert a != RRuntime.STRING_NA; + assert b != RRuntime.STRING_NA; + if (identityEquals.profile(a == b)) { + return true; + } else { + return a.startsWith(b); + } + } + } + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java new file mode 100644 index 0000000000000000000000000000000000000000..4bb40e51c186ac1324752ef334b2bbdb8e0a1999 --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.r.nodes.profile.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.ops.na.*; + +/** + * Primitive indexed N-dimensional vector write node. It can be used for vector replaces and + * extracts. The only difference is that replace indexes the left vector and extract indexes the + * right vector. The index direction is indicated with the boolean flag + * {@link #positionsApplyToRight}. + */ +abstract class WriteIndexedVectorNode extends Node { + + private final int dimensionIndex; + private final int totalDimensions; + + /** + * Indicates if the position vectors index into the left or the right vector. This enables us to + * share the same node for vector replaces and vector extracts. + */ + private final boolean positionsApplyToRight; + /** + * If skipNA is true then no action should be invoked for NA values and its indexed + * subdimensions. + */ + private final boolean skipNA; + + private final VectorLengthProfile positionLengthProfile = VectorLengthProfile.create(); + private final VectorLengthProfile positionOffsetProfile = VectorLengthProfile.create(); + private final VectorLengthProfile dimensionValueProfile = VectorLengthProfile.create(); + private final ValueProfile positionClassProfile = ValueProfile.createClassProfile(); + private final NACheck positionNACheck = NACheck.create(); + + @Child private WriteIndexedScalarNode<RAbstractVector, RAbstractContainer> scalarNode; + @Child private WriteIndexedVectorNode innerVectorNode; + + @SuppressWarnings("unchecked") + protected WriteIndexedVectorNode(RType vectorType, int totalDimensions, int dimensionIndex, boolean positionAppliesToRight, boolean skipNA, boolean setListElementAsObject) { + this.scalarNode = (WriteIndexedScalarNode<RAbstractVector, RAbstractContainer>) createIndexedAction(vectorType, setListElementAsObject); + this.dimensionIndex = dimensionIndex; + this.totalDimensions = totalDimensions; + this.positionsApplyToRight = positionAppliesToRight; + this.skipNA = skipNA; + if (dimensionIndex > 0) { + innerVectorNode = WriteIndexedVectorNodeGen.create(vectorType, totalDimensions, dimensionIndex - 1, positionAppliesToRight, skipNA, setListElementAsObject); + } + } + + public static WriteIndexedVectorNode create(RType vectorType, int totalDimensions, boolean positionAppliesToValue, boolean skipNA, boolean setListElementAsObject) { + return WriteIndexedVectorNodeGen.create(vectorType, totalDimensions, totalDimensions - 1, positionAppliesToValue, skipNA, setListElementAsObject); + } + + public NACheck getValueNACheck() { + return scalarNode.valueNACheck; + } + + public void enableValueNACheck(RAbstractContainer vector) { + getValueNACheck().enable(vector); + if (innerVectorNode != null) { + innerVectorNode.enableValueNACheck(vector); + } + } + + public boolean neverSeenNAInValue() { + if (getValueNACheck().neverSeenNA()) { + if (innerVectorNode == null || innerVectorNode.neverSeenNAInValue()) { + return true; + } + } + return false; + } + + public final void apply(RAbstractVector left, int leftLength, // + Object[] positions, RAbstractContainer right, int rightLength, int[] positionTargetDimensions) { + assert left.getLength() == leftLength; + assert right.getLength() == rightLength; + assert totalDimensions == positions.length : "totalDimensions must be constant per vector write node"; + + Object leftStore = left.getInternalStore(); + Object rightStore = right.getInternalStore(); + + int initialPositionOffset; + if (positionsApplyToRight) { + initialPositionOffset = rightLength; + } else { + initialPositionOffset = leftLength; + } + + int firstTargetDimension; + if (totalDimensions == 0 || positionTargetDimensions == null) { + // no dimensions + firstTargetDimension = initialPositionOffset; + } else { + firstTargetDimension = dimensionValueProfile.profile(positionTargetDimensions[dimensionIndex]); + } + + applyImpl(left, leftStore, 0, leftLength, positionTargetDimensions, firstTargetDimension, // + positions, initialPositionOffset, // + right, rightStore, 0, rightLength, false); + } + + private int applyImpl(// + RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension, // + Object[] positions, int positionOffset, // + RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA) { + + Object position = positionClassProfile.profile(positions[dimensionIndex]); + + int positionLength = getPositionLength(position); + int newPositionOffset; + if (positionOffset == targetDimension) { + newPositionOffset = 1; + } else { + newPositionOffset = positionOffsetProfile.profile(positionOffset / targetDimension); + } + return execute(left, leftStore, leftBase, leftLength, targetDimensions, targetDimension, // + positions, position, newPositionOffset, positionLength, // + right, rightStore, rightBase, rightLength, parentNA); + } + + public int getPositionLength(Object position) { + if (position instanceof RAbstractVector) { + return positionLengthProfile.profile(((RAbstractVector) position).getLength()); + } else { + return -1; + } + } + + protected abstract int execute(RAbstractVector left, Object leftStore, int storeBase, int storeLength, Object targetDimensions, int targetDimension, // + Object[] positions, Object position, int positionOffset, int positionLength, // + RAbstractContainer right, Object rightStore, int valueBase, int valueLength, boolean parentNA); + + @SuppressWarnings("unused") + @Specialization + protected int doMissing(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension, // + Object[] positions, RMissing position, int positionOffset, int positionLength, // + RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA// + ) { + int rightIndex = rightBase; + for (int positionValue = 0; positionValue < targetDimension; positionValue += 1) { + rightIndex = applyInner(// + left, leftStore, leftBase, leftLength, targetDimensions, // + positions, positionOffset, positionValue, // + right, rightStore, rightLength, rightIndex, parentNA); + } + return rightIndex; + } + + @Specialization + protected int doLogicalPosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, int targetDimension, // + Object[] positions, RAbstractLogicalVector position, int positionOffset, int positionLength, // + RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA, // + @Cached("create()") BranchProfile wasTrue, @Cached("create()") BranchProfile outOfBounds) { + positionNACheck.enable(!skipNA && !position.isComplete()); + + int length = targetDimension; + if (positionLength > targetDimension) { + outOfBounds.enter(); + length = positionLength; + } + + int rightIndex = rightBase; + if (positionLength > 0) { + int positionIndex = 0; + for (int i = 0; i < length; i++) { + byte positionValue = position.getDataAt(positionIndex); + boolean isNA = positionNACheck.check(positionValue); + if (isNA || positionValue == RRuntime.LOGICAL_TRUE) { + wasTrue.enter(); + if (outOfBounds.isVisited() && i >= targetDimension) { + isNA = true; + } + rightIndex = applyInner(// + left, leftStore, leftBase, leftLength, targetDimensions, // + positions, positionOffset, i, // + right, rightStore, rightLength, rightIndex, isNA || parentNA); + } + positionIndex = Utils.incMod(positionIndex, positionLength); + } + } + return rightIndex; + } + + /** + * For integer sequences we need to make sure that start and stride is profiled. + * + * @throws SlowPathException + */ + @Specialization(rewriteOn = SlowPathException.class) + protected int doIntegerSequencePosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, @SuppressWarnings("unused") int targetDimension, // + Object[] positions, RIntSequence position, int positionOffset, int positionLength, // + RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA, // + @Cached("create()") IntValueProfile startProfile, // + @Cached("create()") IntValueProfile strideProfile, // + @Cached("createBinaryProfile()") ConditionProfile conditionProfile) throws SlowPathException { + // skip NA check. sequences never contain NA values. + int rightIndex = rightBase; + int start = startProfile.profile(position.getStart() - 1); + int stride = strideProfile.profile(position.getStride()); + int end = start + positionLength * stride; + + if (start < 0 || end <= 0) { + throw new SlowPathException("rewrite to doIntegerPosition"); + } + + boolean ascending = conditionProfile.profile(start < end); + for (int positionValue = start; ascending ? positionValue < end : positionValue > end; positionValue += stride) { + rightIndex = applyInner(// + left, leftStore, leftBase, leftLength, targetDimensions, // + positions, positionOffset, positionValue, // + right, rightStore, rightLength, rightIndex, parentNA); + } + return rightIndex; + } + + /** + * Integer vectors iterate over the number of positions because we assume that the number of + * positions in an integer vector is significantly lower than the number of elements in the + * store. This might not be always true and could benefit from more investigation. + */ + @Specialization(contains = "doIntegerSequencePosition") + protected int doIntegerPosition(RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, @SuppressWarnings("unused") int targetDimension, // + Object[] positions, RAbstractIntVector position, int positionOffset, int positionLength, // + RAbstractContainer right, Object rightStore, int rightBase, int rightLength, boolean parentNA // + ) { + positionNACheck.enable(position); + int rightIndex = rightBase; + + for (int i = 0; i < positionLength; i++) { + int positionValue = position.getDataAt(i); + boolean isNA = positionNACheck.check(positionValue); + if (isNA) { + if (skipNA) { + continue; + } + } + rightIndex = applyInner(// + left, leftStore, leftBase, leftLength, targetDimensions, // + positions, positionOffset, positionValue - 1, // + right, rightStore, rightLength, rightIndex, isNA || parentNA); + } + return rightIndex; + } + + private int applyInner(// + RAbstractVector left, Object leftStore, int leftBase, int leftLength, Object targetDimensions, // + Object[] positions, int positionOffset, int positionValue, // + RAbstractContainer right, Object rightStore, int rightLength, int rightIndex, boolean isNA) { + int newTargetIndex = leftBase + positionValue * positionOffset; + if (dimensionIndex == 0) { + // for-loops leaf for innermost dimension + + // if position indexes value we just need to switch indices + int actionLeftIndex; + int actionRightIndex; + int actionRightMod; + if (positionsApplyToRight) { + actionLeftIndex = rightIndex; + actionRightIndex = newTargetIndex; + actionRightMod = leftLength; + } else { + actionLeftIndex = newTargetIndex; + actionRightIndex = rightIndex; + actionRightMod = rightLength; + } + if (isNA) { + left.setNA(leftStore, actionLeftIndex); + getValueNACheck().seenNA(); + } else { + scalarNode.apply(left, leftStore, actionLeftIndex, right, rightStore, actionRightIndex); + } + + return Utils.incMod(rightIndex, actionRightMod); + } else { + // generate another for-loop for other dimensions + int nextTargetDimension = innerVectorNode.dimensionValueProfile.profile(((int[]) targetDimensions)[innerVectorNode.dimensionIndex]); + return innerVectorNode.applyImpl(// + left, leftStore, newTargetIndex, leftLength, targetDimensions, nextTargetDimension, // + positions, positionOffset, // + right, rightStore, rightIndex, rightLength, isNA); + } + } + + private static WriteIndexedScalarNode<? extends RAbstractVector, ? extends RAbstractContainer> createIndexedAction(RType type, boolean setListElementAsObject) { + switch (type) { + case Logical: + return new WriteLogicalAction(); + case Integer: + return new WriteIntegerAction(); + case Double: + return new WriteDoubleAction(); + case Complex: + return new WriteComplexAction(); + case Character: + return new WriteCharacterAction(); + case Raw: + return new WriteRawAction(); + case Language: + case DataFrame: + case Expression: + case PairList: + case List: + return new WriteListAction(setListElementAsObject); + default: + throw RInternalError.shouldNotReachHere(); + } + } + + private abstract static class WriteIndexedScalarNode<A extends RAbstractVector, V extends RAbstractContainer> extends Node { + + final NACheck valueNACheck = NACheck.create(); + + abstract void apply(A leftAccess, Object leftStore, int leftIndex, V rightAccess, Object rightStore, int rightIndex); + + } + + private static final class WriteLogicalAction extends WriteIndexedScalarNode<RAbstractLogicalVector, RAbstractLogicalVector> { + + @Override + void apply(RAbstractLogicalVector leftAccess, Object leftStore, int leftIndex, RAbstractLogicalVector rightAccess, Object rightStore, int rightIndex) { + byte value = rightAccess.getDataAt(rightStore, rightIndex); + leftAccess.setDataAt(leftStore, leftIndex, value); + valueNACheck.check(value); + } + } + + private static final class WriteIntegerAction extends WriteIndexedScalarNode<RAbstractIntVector, RAbstractIntVector> { + + @Override + void apply(RAbstractIntVector leftAccess, Object leftStore, int leftIndex, RAbstractIntVector rightAccess, Object rightStore, int rightIndex) { + int value = rightAccess.getDataAt(rightStore, rightIndex); + leftAccess.setDataAt(leftStore, leftIndex, value); + valueNACheck.check(value); + } + } + + private static final class WriteDoubleAction extends WriteIndexedScalarNode<RAbstractDoubleVector, RAbstractDoubleVector> { + + @Override + void apply(RAbstractDoubleVector leftAccess, Object leftStore, int leftIndex, RAbstractDoubleVector rightAccess, Object rightStore, int rightIndex) { + double value = rightAccess.getDataAt(rightStore, rightIndex); + leftAccess.setDataAt(leftStore, leftIndex, value); + valueNACheck.check(value); + } + } + + private static final class WriteComplexAction extends WriteIndexedScalarNode<RAbstractComplexVector, RAbstractComplexVector> { + + @Override + void apply(RAbstractComplexVector leftAccess, Object leftStore, int leftIndex, RAbstractComplexVector rightAccess, Object rightStore, int rightIndex) { + RComplex value = rightAccess.getDataAt(rightStore, rightIndex); + leftAccess.setDataAt(leftStore, leftIndex, value); + valueNACheck.check(value); + } + } + + private static final class WriteCharacterAction extends WriteIndexedScalarNode<RAbstractStringVector, RAbstractStringVector> { + + @Override + void apply(RAbstractStringVector leftAccess, Object leftStore, int leftIndex, RAbstractStringVector rightAccess, Object rightStore, int rightIndex) { + String value = rightAccess.getDataAt(rightStore, rightIndex); + leftAccess.setDataAt(leftStore, leftIndex, value); + valueNACheck.check(value); + } + } + + private static final class WriteRawAction extends WriteIndexedScalarNode<RAbstractRawVector, RAbstractRawVector> { + + @Override + void apply(RAbstractRawVector leftAccess, Object leftStore, int leftIndex, RAbstractRawVector rightAccess, Object rightStore, int rightIndex) { + byte value = rightAccess.getRawDataAt(rightStore, rightIndex); + leftAccess.setRawDataAt(leftStore, leftIndex, value); + valueNACheck.check(value); + } + } + + private static final class WriteListAction extends WriteIndexedScalarNode<RAbstractListVector, RAbstractContainer> { + + private final boolean setListElementAsObject; + + public WriteListAction(boolean setListElementAsObject) { + this.setListElementAsObject = setListElementAsObject; + } + + @Override + void apply(RAbstractListVector leftAccess, Object leftStore, int leftIndex, RAbstractContainer rightAccess, Object rightStore, int rightIndex) { + Object rightValue; + if (setListElementAsObject) { + rightValue = rightAccess; + if (rightValue instanceof RAbstractVector) { + // TODO we should unbox instead of materialize here + rightValue = ((RAbstractVector) rightValue).materialize(); + } + } else { + rightValue = rightAccess.getDataAtAsObject(rightStore, rightIndex); + } + leftAccess.setDataAt(leftStore, leftIndex, rightValue); + valueNACheck.checkListElement(rightValue); + } + } + +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java index 1dbadbcd7ec3f3b4c06bfe4eaab12171590d99fd..33ac9867a2bc25bb612f799627b035fed5a46a73 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java @@ -56,22 +56,27 @@ public abstract class CastTypeNode extends BinaryNode { @TruffleBoundary public static CastNode createCast(RType type) { + return createCast(type, false, false, false); + } + + @TruffleBoundary + public static CastNode createCast(RType type, boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) { switch (type) { case Character: - return CastStringNodeGen.create(false, false, false, false); + return CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes, false); case Complex: - return CastComplexNodeGen.create(false, false, false); + return CastComplexNodeGen.create(preserveNames, preserveDimensions, preserveAttributes); case Double: case Numeric: - return CastDoubleNodeGen.create(false, false, false); + return CastDoubleNodeGen.create(preserveNames, preserveDimensions, preserveAttributes); case Integer: - return CastIntegerNodeGen.create(false, false, false); + return CastIntegerNodeGen.create(preserveNames, preserveDimensions, preserveAttributes); case Logical: - return CastLogicalNodeGen.create(false, false, false); + return CastLogicalNodeGen.create(preserveNames, preserveDimensions, preserveAttributes); case Raw: - return CastRawNodeGen.create(false, false, false); + return CastRawNodeGen.create(preserveNames, preserveDimensions, preserveAttributes); case List: - return CastListNodeGen.create(false, false, false); + return CastListNodeGen.create(preserveNames, preserveDimensions, preserveAttributes); default: return null; 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 2e169414177614011bbf4f9822368e59c7c6f5d9..91b24aef9cba508c181947fbce15c5104827a7a5 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 @@ -23,6 +23,7 @@ package com.oracle.truffle.r.nodes.control; import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.env.*; @@ -32,7 +33,7 @@ import com.oracle.truffle.r.runtime.nodes.*; * A {@link BlockNode} represents a sequence of statements such as the body of a {@code while} loop. * */ -public class BlockNode extends SequenceNode implements RSyntaxNode { +public class BlockNode extends SequenceNode implements RSyntaxNode, VisibilityController { public static final RNode[] EMPTY_BLOCK = new RNode[0]; public BlockNode(SourceSection src, RNode[] sequence) { @@ -47,6 +48,12 @@ public class BlockNode extends SequenceNode implements RSyntaxNode { this(src, convert(node)); } + @Override + public Object execute(VirtualFrame frame) { + controlVisibility(); + return super.execute(frame); + } + /** * Ensures that {@code node} is a {@link BlockNode}. */ @@ -89,7 +96,7 @@ public class BlockNode extends SequenceNode implements RSyntaxNode { * it is represented as a LANGSXP with symbol "{" and a NULL cdr, representing the empty * sequence. This is an unpleasant special case in FastR that we can only detect by * re-examining the original source. - * + * * A sequence of length 1, i.e. a single statement, is represented as itself, e.g. a SYMSXP * for "x" or a LANGSXP for a function call. Otherwise, the representation is a LISTSXP * pairlist, where the car is the statement and the cdr is either NILSXP or a LISTSXP for diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java index 718773b5c373be24c61b470616700538a00907ef..5d794f9ba23508b18fc9ee5941336f0398e50eee 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java @@ -33,7 +33,8 @@ import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode; import com.oracle.truffle.r.nodes.access.variables.*; import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.Engine.ParseException; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.gnur.*; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java index 91ab933db03b9618e59e4895c3a5df864db58362..54ebe6c5e62599843a346857ee7faa473ac56c97 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java @@ -85,14 +85,8 @@ public final class ReplacementNode extends RNode implements RSyntaxNode { state.endNodeDeparse(this); } - private static ReplacementNode current; - @Override public void serializeImpl(RSerialize.State state) { - if (this == current) { - throw RInternalError.shouldNotReachHere("replacement recursion"); - } - current = this; syntaxAST.serializeImpl(state); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java index e3f98636925e9c3f474f2991e1addd42c1c2f889..1d59cb4c12c7be8d7a54735304548f8a92226a0f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallArgumentsNode.java @@ -207,6 +207,29 @@ public class CallArgumentsNode extends ArgumentsNode { return new RArgsValuesAndNames(values, resultSignature); } + @ExplodeLoop + public Object[] evaluateFlattenObjects(VirtualFrame frame) { + int size = arguments.length; + RArgsValuesAndNames varArgInfo = null; + if (containsVarArgsSymbol()) { + varArgInfo = getVarargsAndNames(frame); + size += (varArgInfo.getLength() - 1) * varArgsSymbolIndices.length; + } + Object[] values = new Object[size]; + int vargsSymbolsIndex = 0; + int index = 0; + for (int i = 0; i < arguments.length; i++) { + if (vargsSymbolsIndex < varArgsSymbolIndices.length && varArgsSymbolIndices[vargsSymbolsIndex] == i) { + index = flattenVarArgsObject(frame, varArgInfo, values, index); + vargsSymbolsIndex++; + } else { + values[index] = arguments[i] == null ? RMissing.instance : arguments[i].execute(frame); + index++; + } + } + return values; + } + private int flattenVarArgs(VirtualFrame frame, RArgsValuesAndNames varArgInfo, String[] names, Object[] values, int startIndex) { int index = startIndex; for (int j = 0; j < varArgInfo.getLength(); j++) { @@ -251,4 +274,17 @@ public class CallArgumentsNode extends ArgumentsNode { } return result; } + + private int flattenVarArgsObject(VirtualFrame frame, RArgsValuesAndNames varArgInfo, Object[] values, int startIndex) { + int index = startIndex; + for (int j = 0; j < varArgInfo.getLength(); j++) { + if (promiseHelper == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + promiseHelper = insert(new PromiseCheckHelperNode()); + } + values[index] = promiseHelper.checkEvaluate(frame, varArgInfo.getArgument(j)); + index++; + } + return index; + } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java index ccab18e3cbeb3a2a421d9db5024e74fa66d97e92..09d68b7b14f11dfa534a1e37b247b1108437be86 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java @@ -91,13 +91,7 @@ public abstract class CallMatcherNode extends RBaseNode { } protected Object[] prepareArguments(VirtualFrame frame, Object[] reorderedArgs, ArgumentsSignature reorderedSignature, RFunction function, S3Args s3Args) { - RCaller caller = RArguments.getCall(frame); - MaterializedFrame callerFrame = RArguments.getCallerFrame(frame); - if (caller == null && callerFrame == null) { - callerFrame = RArguments.getEnvironment(frame).getFrame(); - assert callerFrame != null; - } - return argsNode.execute(function, caller, callerFrame, RArguments.getDepth(frame) + 1, reorderedArgs, reorderedSignature, s3Args); + return argsNode.execute(function, RDataFactory.createCaller(this), null, RArguments.getDepth(frame) + 1, reorderedArgs, reorderedSignature, s3Args); } protected final void evaluatePromises(VirtualFrame frame, RFunction function, Object[] args, int varArgIndex) { 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 0aa2795c813d636d9f997854be36e0e6b5d1f7b8..ffad550f94cd6b039310244e1daf041dc3e39e9b 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 @@ -41,6 +41,7 @@ import com.oracle.truffle.r.nodes.instrument.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RArguments.S3Args; import com.oracle.truffle.r.runtime.Utils.DebugExitException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.env.frame.*; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java index 87c16c2c611d24c5461d786dc8f1ece20fd9386c..22e8e9885369d4a54ad5b0e08c48cd51a0e859f6 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchNode.java @@ -24,6 +24,7 @@ import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result; import com.oracle.truffle.r.nodes.runtime.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RArguments.S3Args; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.nodes.*; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java index 7d74aa810f71b56ed87e6602761be15edc419dd7..d85c38664f18a927909b556f75a9293a43abd558 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseHelperNode.java @@ -30,8 +30,12 @@ import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; -import com.oracle.truffle.r.runtime.data.RPromise.*; +import com.oracle.truffle.r.runtime.data.RPromise.EagerPromise; +import com.oracle.truffle.r.runtime.data.RPromise.OptType; +import com.oracle.truffle.r.runtime.data.RPromise.PromiseType; +import com.oracle.truffle.r.runtime.data.RPromise.VarargPromise; import com.oracle.truffle.r.runtime.nodes.*; /** 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 0755a6322e444a7d21e61719ed8a83ec74c3fc56..b8cbf5928b27cc0ab1f3bd138c22a55ba303a65b 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 @@ -28,6 +28,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.utilities.*; @@ -209,6 +210,11 @@ public final class RCallNode extends RNode implements RSyntaxNode { private final ConditionProfile isPromiseProfile = ConditionProfile.createBinaryProfile(); private final BranchProfile normalDispatchProfile = BranchProfile.create(); private final BranchProfile errorProfile = BranchProfile.create(); + private final ConditionProfile isRFunctionProfile = ConditionProfile.createBinaryProfile(); + + @Child private Node foreignCall; + @CompilationFinal private int foreignCallArgCount; + @Child private CallArgumentsNode foreignCallArguments; public RCallNode(RNode function, RSyntaxNode[] arguments, ArgumentsSignature signature) { this.functionNode = function; @@ -225,7 +231,28 @@ public final class RCallNode extends RNode implements RSyntaxNode { return execute(frame, executeFunctionNode(frame)); } - public Object execute(VirtualFrame frame, RFunction function) { + public Object execute(VirtualFrame frame, Object functionObject) { + RFunction function; + if (isRFunctionProfile.profile(functionObject instanceof RFunction)) { + function = (RFunction) functionObject; + // fall through + } else if (functionObject instanceof TruffleObject && !(functionObject instanceof RTypedValue)) { + if (foreignCallArguments == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + foreignCallArguments = insert(createArguments(null, true, true)); + } + Object[] argumentsArray = foreignCallArguments.evaluateFlattenObjects(frame); + if (foreignCall == null || foreignCallArgCount != argumentsArray.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + foreignCall = insert(Message.createExecute(argumentsArray.length).createNode()); + foreignCallArgCount = argumentsArray.length; + } + return ForeignAccess.execute(foreignCall, frame, (TruffleObject) functionObject, argumentsArray); + } else { + errorProfile.enter(); + throw RError.error(this, RError.Message.APPLY_NON_FUNCTION); + } + if (!signature.isEmpty() && function.getRBuiltin() != null) { RBuiltinDescriptor builtin = builtinProfile.profile(function.getRBuiltin()); if (builtin.getDispatch() == RDispatch.INTERNAL_GENERIC) { @@ -270,7 +297,7 @@ public final class RCallNode extends RNode implements RSyntaxNode { return call.execute(frame, function, null); } - private RFunction executeFunctionNode(VirtualFrame frame) { + private Object executeFunctionNode(VirtualFrame frame) { /** * Expressions such as "pkg:::f" can result in (delayedAssign) promises that must be * explicitly resolved. @@ -283,12 +310,7 @@ public final class RCallNode extends RNode implements RSyntaxNode { } value = promiseHelper.evaluate(frame, (RPromise) value); } - if (value instanceof RFunction) { - return (RFunction) value; - } else { - errorProfile.enter(); - throw RError.error(this, RError.Message.APPLY_NON_FUNCTION); - } + return value; } public RNode getFunctionNode() { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java index bfd848701103cd94a9ed534be28212906235080a..a83085214806b9bbda39c878f2d46ebac19c2d07 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java @@ -22,6 +22,7 @@ import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.access.variables.*; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode.ReadKind; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.nodes.*; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java index c75307c7fa3f8074ec3a83eb34493240ba45a8ae..d7212fe3176e5de82bee2457b885de827561847e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java @@ -33,7 +33,7 @@ public final class UseMethodInternalNode extends RNode implements VisibilityCont public UseMethodInternalNode(String generic, ArgumentsSignature signature, boolean wrap) { this.generic = generic; this.signature = signature; - this.wrap = wrap && FastROptions.InvisibleArgs.getValue(); + this.wrap = wrap && FastROptions.InvisibleArgs; } public Object execute(VirtualFrame frame, RAbstractContainer dispatchOn, Object[] arguments) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java index 564042082bd50a2af18ae0a44295c4f47142c177..9c692fb85ca1da073ca1bed3ca8d1ac2afea17cd 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/EagerEvalHelper.java @@ -39,28 +39,28 @@ public class EagerEvalHelper { * @return Whether to use optimizations for constants */ public static boolean optConsts() { - return FastROptions.EagerEval.getValue() || FastROptions.EagerEvalConstants.getValue(); + return FastROptions.EagerEval || FastROptions.EagerEvalConstants; } /** * @return Whether to use optimizations for single variables */ public static boolean optVars() { - return FastROptions.EagerEval.getValue() || FastROptions.EagerEvalVariables.getValue(); + return FastROptions.EagerEval || FastROptions.EagerEvalVariables; } /** * @return Whether to use optimizations for single variables */ public static boolean optDefault() { - return FastROptions.EagerEval.getValue() || FastROptions.EagerEvalDefault.getValue(); + return FastROptions.EagerEval || FastROptions.EagerEvalDefault; } /** * @return Whether to use optimizations for arbitrary expressions */ public static boolean optExprs() { - return FastROptions.EagerEval.getValue() || FastROptions.EagerEvalExpressions.getValue(); + return FastROptions.EagerEval || FastROptions.EagerEvalExpressions; } public static boolean isOptimizableConstant(RNode expr) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RInstrument.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RInstrument.java index 47d17b49b3dab0e7ac80a84d00ce82c3774c5144..46891d253b0c037e42037d983232af25eef3bf58 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RInstrument.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/RInstrument.java @@ -177,7 +177,7 @@ public class RInstrument { } } else if (tag == StandardSyntaxTag.START_METHOD) { putProbe((FunctionUID) tagValue, probe); - if (FastROptions.TraceCalls.getValue()) { + if (FastROptions.TraceCalls) { TraceHandling.attachTraceHandler((FunctionUID) tagValue); } } else if (tag == StandardSyntaxTag.STATEMENT) { @@ -236,18 +236,15 @@ public class RInstrument { * features that need it are also enabled. */ public static void initialize() { - // @formatter:off - instrumentingEnabled = FastROptions.Instrument.getValue() || FastROptions.TraceCalls.getValue() || FastROptions.Rdebug.getValue() != null || - REntryCounters.Function.enabled() || RNodeTimer.Statement.enabled(); - // @formatter:on + instrumentingEnabled = FastROptions.Instrument || FastROptions.TraceCalls || FastROptions.Rdebug != null || REntryCounters.Function.enabled() || RNodeTimer.Statement.enabled(); if (instrumentingEnabled) { Probe.registerASTProber(RASTProber.getRASTProber()); Probe.addProbeListener(new RProbeListener()); } - if (instrumentingEnabled || FastROptions.LoadPkgSourcesIndex.getValue()) { + if (instrumentingEnabled || FastROptions.LoadPkgSourcesIndex) { RPackageSource.initialize(); } - String rdebugValue = FastROptions.Rdebug.getValue(); + String rdebugValue = FastROptions.Rdebug; if (rdebugValue != null) { debugFunctionNames = rdebugValue.split(","); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java index 8cf0c6fa82f1bff5fbb3a020e5e29d51a9775104..629f012a9db494d8203e906b1a3e42db9a72d3bc 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/Browser.java @@ -26,6 +26,8 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; import com.oracle.truffle.r.runtime.data.*; /** @@ -45,7 +47,7 @@ public class Browser { @TruffleBoundary public static ExitMode interact(MaterializedFrame frame) { - RContext.ConsoleHandler ch = RContext.getInstance().getConsoleHandler(); + ConsoleHandler ch = RContext.getInstance().getConsoleHandler(); String savedPrompt = ch.getPrompt(); ch.setPrompt(browserPrompt(RArguments.getDepth(frame))); ExitMode exitMode = ExitMode.NEXT; @@ -53,7 +55,7 @@ public class Browser { LW: while (true) { String input = ch.readLine().trim(); if (input.length() == 0) { - RLogicalVector browserNLdisabledVec = (RLogicalVector) RContext.getROptionsState().getValue("browserNLdisabled"); + RLogicalVector browserNLdisabledVec = (RLogicalVector) RContext.getInstance().stateROptions.getValue("browserNLdisabled"); if (!RRuntime.fromLogical(browserNLdisabledVec.getDataAt(0))) { input = lastEmptyLineCommand; } @@ -89,7 +91,14 @@ public class Browser { } default: - RContext.getEngine().parseAndEval(Source.fromText(input, BROWSER_SOURCE), frame, true, false); + try { + RContext.getEngine().parseAndEval(Source.fromText(input, BROWSER_SOURCE), frame, true); + } catch (ReturnException e) { + exitMode = ExitMode.NEXT; + break LW; + } catch (ParseException e) { + throw e.throwAsRError(); + } break; } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/DebugHandling.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/DebugHandling.java index c69ca9c8da90ef41efd0d0223f717c26c779bc3a..7c9a806706f1a0ee9641e7511820d27be5186376 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/DebugHandling.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/debug/DebugHandling.java @@ -35,6 +35,7 @@ import com.oracle.truffle.r.nodes.control.*; import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.nodes.instrument.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.nodes.*; @@ -400,7 +401,7 @@ public class DebugHandling { } private static void printNode(Node node, boolean curly) { - RContext.ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler(); + ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler(); RDeparse.State state = RDeparse.State.createPrintableState(); ((RBaseNode) node).deparse(state); consoleHandler.print("debug: "); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/trace/TraceHandling.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/trace/TraceHandling.java index 32fe70c27f241fcfe8f114fbeba6bf0c50213409..632d498b98c7a592e4f287ba7c42cac37e123f27 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/trace/TraceHandling.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/instrument/trace/TraceHandling.java @@ -32,6 +32,7 @@ import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.r.nodes.function.*; import com.oracle.truffle.r.nodes.instrument.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; public class TraceHandling { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java index 9b0105d24f460d9e9d6ce763b486a010c7dfed25..7c544396a25aac15ba183e0810ed90eba067312a 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java @@ -226,12 +226,7 @@ public final class BinaryMapNode extends RBaseNode { if (target == null) { int maxLength = Math.max(leftLength, rightLength); target = createOrShareVector(leftLength, left, rightLength, right, maxLength); - Object store; - if (target instanceof RAccessibleStore) { - store = ((RAccessibleStore<?>) target).getInternalStore(); - } else { - throw RInternalError.shouldNotReachHere(); - } + Object store = target.getInternalStore(); assert left.getLength() == leftLength; assert right.getLength() == rightLength; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java index 02919e7095040ca8c1f61980424eb4d7a665fe20..a2b6a1ffcd8fd45e18fed553a6e45261d187c6e0 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java @@ -124,12 +124,7 @@ public final class UnaryMapNode extends RBaseNode { } if (target == null) { target = createOrShareVector(operandLength, operand); - Object store; - if (target instanceof RAccessibleStore) { - store = ((RAccessibleStore<?>) target).getInternalStore(); - } else { - throw RInternalError.shouldNotReachHere(); - } + Object store = target.getInternalStore(); vectorNode.apply(scalarNode, store, operandCast, operandLength); RNode.reportWork(this, operandLength); target.setComplete(scalarNode.isComplete()); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/CountedLoopConditionProfile.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/CountedLoopConditionProfile.java new file mode 100644 index 0000000000000000000000000000000000000000..c997c2e4c7b2e6ffb91a6462bc47b26fb457997e --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/CountedLoopConditionProfile.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.nodes.profile; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.*; + +public final class CountedLoopConditionProfile { + + @CompilationFinal private int count; + @CompilationFinal private int sum; + + public void profileLength(int length) { + if (CompilerDirectives.inInterpreter()) { + sum += length; + count++; + } + } + + public boolean inject(boolean condition) { + if (CompilerDirectives.inCompiledCode()) { + double averageLength = (double) sum / (double) count; + return CompilerDirectives.injectBranchProbability(averageLength / (averageLength + 1.0), condition); + } else { + return condition; + } + } + + public static CountedLoopConditionProfile create() { + return new CountedLoopConditionProfile(); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/IntValueProfile.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/IntValueProfile.java index 569d7d027f5df351ea1a772719a1c4296353ca22..26e95f1f5594ffd7a1a546501586383aadaac1bc 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/IntValueProfile.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/IntValueProfile.java @@ -32,8 +32,7 @@ public final class IntValueProfile { private static final byte GENERIC = 2; @CompilationFinal private int cachedValue; - - private byte state = 0; + @CompilationFinal private byte state = 0; private IntValueProfile() { } @@ -47,6 +46,7 @@ public final class IntValueProfile { CompilerDirectives.transferToInterpreterAndInvalidate(); if (state == UNINITIALIZED) { this.cachedValue = value; + this.state = SPECIALIZED; } else { this.state = GENERIC; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/VectorLengthProfile.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/VectorLengthProfile.java index 4c14c6688e780a833560b8a104ed97620bf770c4..a4a439febae2e7d7826d996115b5bc279f72157c 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/VectorLengthProfile.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/profile/VectorLengthProfile.java @@ -55,6 +55,10 @@ public final class VectorLengthProfile { } } + public int getCachedLength() { + return cachedLength; + } + public static VectorLengthProfile create() { return new VectorLengthProfile(); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java index bd515df322a359bac748ab52ee55fd69dfe9de06..2f11fed3829f37d318e8c449d455d803ece7018d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java @@ -77,12 +77,12 @@ public abstract class CastIntegerNode extends CastBaseNode { } @Specialization - protected RIntVector doIntVector(RIntVector operand) { + protected RIntSequence doIntSequence(RIntSequence operand) { return operand; } @Specialization - protected RIntSequence doIntSequence(RIntSequence operand) { + protected RAbstractIntVector doIntVector(RAbstractIntVector operand) { return operand; } @@ -161,7 +161,7 @@ public abstract class CastIntegerNode extends CastBaseNode { } @Specialization - protected RIntVector doComplexVector(RComplexVector operand) { + protected RIntVector doComplexVector(RAbstractComplexVector operand) { naCheck.enable(operand); int length = operand.getLength(); int[] idata = new int[length]; @@ -181,7 +181,7 @@ public abstract class CastIntegerNode extends CastBaseNode { } @Specialization - protected RIntVector doStringVector(RStringVector operand, // + protected RIntVector doStringVector(RAbstractStringVector operand, // @Cached("createBinaryProfile()") ConditionProfile emptyStringProfile) { naCheck.enable(operand); int[] idata = new int[operand.getLength()]; @@ -217,28 +217,28 @@ public abstract class CastIntegerNode extends CastBaseNode { } @Specialization - public RIntVector doLogicalVector(RLogicalVector operand) { + public RIntVector doLogicalVector(RAbstractLogicalVector operand) { return createResultVector(operand, index -> naCheck.convertLogicalToInt(operand.getDataAt(index))); } @Specialization - protected RIntVector doDoubleVector(RDoubleVector operand) { + protected RIntVector doDoubleVector(RAbstractDoubleVector operand) { naCheck.enable(operand); return createResultVector(operand, naCheck.convertDoubleVectorToIntData(operand)); } @Specialization - protected RIntVector doRawVector(RRawVector operand) { + protected RIntVector doRawVector(RAbstractRawVector operand) { return createResultVector(operand, index -> RRuntime.raw2int(operand.getDataAt(index))); } @Specialization - protected RIntVector doList(RList list) { + protected RIntVector doList(RAbstractListVector list) { int length = list.getLength(); int[] result = new int[length]; boolean seenNA = false; for (int i = 0; i < length; i++) { - Object entry = list.getDataAt(i); + Object entry = list.getDataAtAsObject(i); if (entry instanceof RList) { result[i] = RRuntime.INT_NA; seenNA = true; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java index 1f3612ebaba870dde9b0799235bbfeff36091149..5ca10dddd14a13783bde7d2b043372dbef4f77b7 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java @@ -28,6 +28,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute; import com.oracle.truffle.r.runtime.data.model.*; diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java index abd210cac8e9889eeab7706ef1536e5ef8cf6fa1..61472f263347daafc2b1e3ccbc49e389e5308b6d 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/gnfi/GNFI_RFFIFactory.java @@ -30,7 +30,8 @@ import java.util.*; import com.oracle.nfi.*; import com.oracle.nfi.api.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.ContextState; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.RContext.ContextState; import com.oracle.truffle.r.runtime.ffi.*; /** @@ -207,8 +208,8 @@ public class GNFI_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI { throw Utils.fail("glob not implemented"); } - public ContextState newContext(RContext context, Object... objects) { + @Override + public ContextState newContext(RContext context) { throw RInternalError.unimplemented(); } - } diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java index 76648106be8befd9d474e6b942a76b9e14c64a35..c91106200b723abc56eddc37207a5b2a8a49a72e 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java @@ -23,6 +23,7 @@ package com.oracle.truffle.r.runtime.ffi.jnr; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; @@ -308,19 +309,19 @@ public class CallRFFIHelper { } static Object getGlobalEnv() { - return RContext.getREnvironmentState().getGlobalEnv(); + return RContext.getInstance().stateREnvironment.getGlobalEnv(); } static Object getBaseEnv() { - return RContext.getREnvironmentState().getBaseEnv(); + return RContext.getInstance().stateREnvironment.getBaseEnv(); } static Object getBaseNamespace() { - return RContext.getREnvironmentState().getBaseNamespace(); + return RContext.getInstance().stateREnvironment.getBaseNamespace(); } static Object getNamespaceRegistry() { - return RContext.getREnvironmentState().getNamespaceRegistry(); + return RContext.getInstance().stateREnvironment.getNamespaceRegistry(); } static int isInteractive() { diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java index e6f420c416215c781878fd9b187eacb367246552..8168b6dfc50db9c6e86135649bd8114a1257af55 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java @@ -34,8 +34,9 @@ import jnr.posix.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.ContextState; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.RContext.ContextState; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.ffi.*; @@ -68,7 +69,8 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, Stat } - public ContextState newContext(RContext context, Object... objects) { + @Override + public ContextState newContext(RContext context) { return new ContextStateImpl(); } @@ -413,8 +415,7 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, Stat @TruffleBoundary private static Linpack createAndLoadLib() { // need to load blas lib as Fortran functions in appl lib need it - LibraryLoader.create(Linpack.class).load("Rblas"); - return LibraryLoader.create(Linpack.class).load("appl"); + return LibraryLoader.create(Linpack.class).library("Rblas").library("appl").load(); } static Linpack linpack() { @@ -449,7 +450,7 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI, BaseRFFI, Stat public interface Stats { /* * TODO add @In/@Out to any arrays that are known to be either @In or @Out (default is - * + * * @Inout) */ diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java index 0fd9498d675978f3a20a70c5f905813c28c39791..2beeb56b301a351aea20cc04bb574df5b793de98 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java @@ -25,126 +25,103 @@ package com.oracle.truffle.r.runtime; import java.util.*; import java.util.Map.Entry; -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.r.runtime.RCmdOptions.*; - /** * Options to control the behavior of the FastR system, that relate to the implementation, i.e., are - * <b>not</b> part of the standard set of R options or command line options. - * - * Currently it is not possible to include the FastR options in Graal as FastR is not part of Graal. - * Someday it may be possible to register such options with the Graal VM, but in the interim, we - * override the implementation to use system properties. The syntax follows that for Graal options - * except that a {@code D} (for setting system property) must be the first character,e.g. - * {@code -DR:+OptionName} to set a boolean valued option to {@code true}. + * <b>not</b> part of the standard set of R options or command line options. The syntax follows that + * for Graal options except that a {@code D} (for setting system property) must be the first + * character,e.g. {@code -DR:+OptionName} to set a boolean valued option to {@code true}. */ public class FastROptions { - private static final List<Option<?>> optionsList = new ArrayList<>(); - - //@formatter:off - public static final Option<Boolean> PrintErrorStacktraces = newBooleanOption("PrintErrorStacktraces", false, "Prints Java and R stack traces for all errors"); - public static final Option<Boolean> PrintErrorStacktracesToFile = newBooleanOption("PrintErrorStacktracesToFile", true, "Dumps Java and R stack traces to file for all errors"); - public static final Option<Boolean> CheckResultCompleteness = newBooleanOption("CheckResultCompleteness", true, "Assert completeness of results vectors after evaluating unit tests and R shell commands"); - public static final Option<String> Debug = newStringOption("Debug", null, "Debug=name1,name2,...; Turn on debugging output for 'name1', 'name2', etc."); - public static final Option<Boolean> Instrument = newBooleanOption("Instrument", false, "Enable Instrumentation"); - public static final Option<Boolean> TraceCalls = newBooleanOption("TraceCalls", false, "Trace all R function calls (implies +Instrument)"); - public static final Option<String> PerfStats = newStringOption("PerfStats", null, "PerfStats=p1,p2,...; Collect performance stats identified by p1, etc."); - public static final Option<String> PerfStatsFile = newStringOption("PerfStatsFile", null, "PerfStatsFile=file; Send performance stats to 'file', default stdout"); - public static final Option<String> Rdebug = newStringOption("Rdebug", null, "Rdebug=f1,f2.,,,; list of R function to call debug on (implies +Instrument)"); - public static final Option<Boolean> PerformanceWarnings = newBooleanOption("PerformanceWarnings", false, "Print FastR performance warning"); - public static final Option<Boolean> LoadBase = newBooleanOption("LoadBase", true, "Load base package"); - public static final Option<Boolean> PrintComplexLookups = newBooleanOption("PrintComplexLookups", false, "Print a message for each non-trivial variable lookup"); - public static final Option<Boolean> IgnoreVisibility = newBooleanOption("IgnoreVisibility", false, "Ignore setting of the visibility flag"); - public static final Option<Boolean> LoadPkgSourcesIndex = newBooleanOption("LoadPkgSourcesIndex", true, "Load R package sources index"); - public static final Option<Boolean> InvisibleArgs = newBooleanOption("InvisibleArgs", true, "Argument writes do not trigger state transitions"); - public static final Option<Boolean> ExperimentalStateTrans = newBooleanOption("ExperimentalStateTrans", true, "Eperimental state transition implementation"); - public static final Option<Boolean> RefCountIncOnly = newBooleanOption("RefCountIncOnly", false, "Disable reference count decrements for eperimental state transition implementation"); - - // Promises optimizations - public static final Option<Boolean> EagerEval = newBooleanOption("EagerEval", false, "If enabled, overrides all other EagerEval switches (see EagerEvalHelper)"); - public static final Option<Boolean> EagerEvalConstants = newBooleanOption("EagerEvalConstants", true, "Unconditionally evaluates constants before creating Promises"); - public static final Option<Boolean> EagerEvalVariables = newBooleanOption("EagerEvalVariables", true, "Enables optimistic eager evaluation of single variables reads"); - public static final Option<Boolean> EagerEvalDefault = newBooleanOption("EagerEvalDefault", false, "Enables optimistic eager evaluation of single variables reads (for default parameters)"); - public static final Option<Boolean> EagerEvalExpressions = newBooleanOption("EagerEvalExpressions", false, "Enables optimistic eager evaluation of trivial expressions"); - //@formatter:on - - @CompilationFinal public static boolean NewStateTransition; - @CompilationFinal public static boolean RefCountIncrementOnly; - - private static boolean initialized; - - public static Option<Boolean> newBooleanOption(String name, boolean defaultValue, String help) { - Option<Boolean> option = new Option<>(true, OptionType.BOOLEAN, null, name, help, defaultValue); - optionsList.add(option); - return option; - } - - public static Option<String> newStringOption(String name, String defaultValue, String help) { - Option<String> option = new Option<>(null, true, OptionType.STRING, null, name, help, defaultValue); - optionsList.add(option); - return option; - } - public static void initialize() { - if (initialized) { - return; - } - CompilerAsserts.neverPartOfCompilation(); - initialized = true; + private static Map<String, String> options = new HashMap<>(); + private static boolean printHelp; + static { for (Entry<Object, Object> entry : System.getProperties().entrySet()) { String prop = (String) entry.getKey(); if (prop.equals("R")) { - printHelp(); - Utils.exit(0); + printHelp = true; } if (prop.startsWith("R:")) { - String optName = optionName(prop); - Option<?> option = findOption(optName); - if (option == null) { - System.err.println(prop + " is not a valid FastR option"); - System.exit(2); - } else { - switch (option.type) { - case BOOLEAN: { - boolean value = booleanOptionValue(prop); - option.setValue(value); - break; - } + options.put(optionName(prop), prop); + } + } + } - case STRING: { - String value = (String) entry.getValue(); - option.setValue(value); - break; - } + public static final boolean PrintErrorStacktraces = parseBooleanOption("PrintErrorStacktraces", false, "Prints Java and R stack traces for all errors"); + public static final boolean PrintErrorStacktracesToFile = parseBooleanOption("PrintErrorStacktracesToFile", true, "Dumps Java and R stack traces to file for all errors"); + public static final boolean CheckResultCompleteness = parseBooleanOption("CheckResultCompleteness", true, "Assert completeness of results vectors after evaluating unit tests and R shell commands"); + public static String Debug = parseStringOption("Debug", null, "Debug=name1,name2,...; Turn on debugging output for 'name1', 'name2', etc."); + public static final boolean Instrument = parseBooleanOption("Instrument", false, "Enable Instrumentation"); + public static final boolean TraceCalls = parseBooleanOption("TraceCalls", false, "Trace all R function calls (implies +Instrument)"); + public static final String PerfStats = parseStringOption("PerfStats", null, "PerfStats=p1,p2,...; Collect performance stats identified by p1, etc."); + public static final String PerfStatsFile = parseStringOption("PerfStatsFile", null, "PerfStatsFile=file; Send performance stats to 'file', default stdout"); + public static final String Rdebug = parseStringOption("Rdebug", null, "Rdebug=f1,f2.,,,; list of R function to call debug on (implies +Instrument)"); + public static final boolean PerformanceWarnings = parseBooleanOption("PerformanceWarnings", false, "Print FastR performance warning"); + public static final boolean LoadBase = parseBooleanOption("LoadBase", true, "Load base package"); + public static final boolean PrintComplexLookups = parseBooleanOption("PrintComplexLookups", false, "Print a message for each non-trivial variable lookup"); + public static final boolean IgnoreVisibility = parseBooleanOption("IgnoreVisibility", false, "Ignore setting of the visibility flag"); + public static final boolean LoadPkgSourcesIndex = parseBooleanOption("LoadPkgSourcesIndex", true, "Load R package sources index"); + public static final boolean InvisibleArgs = parseBooleanOption("InvisibleArgs", true, "Argument writes do not trigger state transitions"); + public static final boolean NewStateTransition = parseBooleanOption("NewStateTransition", true, "Eperimental state transition implementation"); + public static final boolean RefCountIncrementOnly = parseBooleanOption("RefCountIncrementOnly", false, "Disable reference count decrements for eperimental state transition implementation"); + + // Promises optimizations + public static final boolean EagerEval = parseBooleanOption("EagerEval", false, "If enabled, overrides all other EagerEval switches (see EagerEvalHelper)"); + public static final boolean EagerEvalConstants = parseBooleanOption("EagerEvalConstants", true, "Unconditionally evaluates constants before creating Promises"); + public static final boolean EagerEvalVariables = parseBooleanOption("EagerEvalVariables", true, "Enables optimistic eager evaluation of single variables reads"); + public static final boolean EagerEvalDefault = parseBooleanOption("EagerEvalDefault", false, "Enables optimistic eager evaluation of single variables reads (for default parameters)"); + public static final boolean EagerEvalExpressions = parseBooleanOption("EagerEvalExpressions", false, "Enables optimistic eager evaluation of trivial expressions"); + + static { + if (options != null && !options.isEmpty()) { + System.err.println(options.keySet() + " are not valid FastR options"); + System.exit(2); + } + if (printHelp) { + System.exit(0); + } + } - default: - throw RInternalError.shouldNotReachHere(); - } + public static boolean parseBooleanOption(String name, boolean defaultValue, String help) { + if (printHelp) { + System.out.printf("%35s %s (default: %b)\n", "-DR:" + name, help, defaultValue); + return false; + } else { + String optionValue = options.remove(name); + if (optionValue == null) { + return defaultValue; + } else { + switch (optionValue.charAt(2)) { + case '+': + return true; + case '-': + return false; + default: + System.out.println("-DR:[+-]" + name + " expected"); + System.exit(2); + return false; } } } - NewStateTransition = FastROptions.ExperimentalStateTrans.getValue(); - RefCountIncrementOnly = FastROptions.RefCountIncOnly.getValue(); - // debug(); } - private static void printHelp() { - for (Option<?> option : optionsList) { - String helpName = option.plainName; - System.out.printf(" -DR:%s", helpName); - int spaces; - int optLength = helpName.length() + 8; - if (optLength >= 22) { - System.out.println(); - spaces = 22; + public static String parseStringOption(String name, String defaultValue, String help) { + if (printHelp) { + System.out.printf("%35s %s (default: %b)\n", "-DR:" + name, help, defaultValue); + return ""; + } else { + String optionValue = options.remove(name); + if (optionValue == null) { + return defaultValue; } else { - spaces = 22 - optLength; - } - for (int i = 0; i < spaces; i++) { - System.out.print(' '); + int equals = optionValue.indexOf('='); + if (equals == -1) { + System.out.println("-DR:" + name + "=value expected"); + System.exit(2); + return null; + } + return optionValue.substring(equals + 1); } - System.out.println(option.help); } } @@ -174,18 +151,16 @@ public class FastROptions { * @return {@code ""} if the option is set with no {@code =value} component, the element if * {@code element} matches an element, {@code null} otherwise. */ - public static String matchesElement(String element, Option<String> stringOption) { - initialize(); - String s = stringOption.getValue(); - if (s == null) { + public static String matchesElement(String element, String option) { + if (option == null) { return null; - } else if (s.length() == 0) { - return s; + } else if (option.length() == 0) { + return option; } else { - String[] parts = s.split(","); + String[] parts = option.split(","); for (String part : parts) { if (part.startsWith(element)) { - return s; + return option; } } } @@ -197,7 +172,7 @@ public class FastROptions { * use-case. */ public static void debugUpdate(String element) { - String s = Debug.getValue(); + String s = Debug; if (s == null) { // nothing was set s = element; @@ -213,16 +188,7 @@ public class FastROptions { } s = s + "," + element; } - Debug.setValue(s); - } - - private static Option<?> findOption(String key) { - for (Option<?> option : optionsList) { - if (option.plainName.equals(key)) { - return option; - } - } - return null; + Debug = s; } private static String optionName(String key) { @@ -235,17 +201,4 @@ public class FastROptions { return s; } } - - private static boolean booleanOptionValue(String option) { - return option.charAt(2) == '+'; - } - - @SuppressWarnings("unused") - private static void debug() { - for (Option<?> option : optionsList) { - System.out.printf("%s: ", option.plainName); - System.out.println(option.getValue()); - } - } - } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java index 8890bf4e7a23152702f31505baa7c48b857c98ea..82156504f7329ceb6055bbf9a374c84cb1765ba4 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java @@ -305,6 +305,11 @@ public final class RArguments { */ public static Frame unwrap(Frame frame) { Object[] arguments = frame.getArguments(); - return arguments.length == 1 ? (Frame) arguments[0] : frame; + return arguments.length == 1 && arguments[0] instanceof Frame ? (Frame) arguments[0] : frame; + } + + public static boolean isRFrame(Frame frame) { + Object[] arguments = frame.getArguments(); + return arguments.length >= MINIMAL_ARRAY_LENGTH && (arguments[INDEX_ENVIRONMENT] instanceof REnvironment || arguments[INDEX_FUNCTION] instanceof RFunction); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java index dad360bda10a469e49011b1084c2a3db844b0556..ffc0d94ddb73f7267e04a4b1476cbcb3b8a6d58f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java @@ -25,8 +25,10 @@ package com.oracle.truffle.r.runtime; import java.io.*; import java.util.concurrent.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.r.runtime.conn.*; import com.oracle.truffle.r.runtime.data.*; -import com.oracle.truffle.r.runtime.data.model.*; +import com.oracle.truffle.r.runtime.env.*; /** * Implementation of a channel abstraction used for communication between parallel contexts in @@ -138,19 +140,74 @@ public class RChannel { } } - public static void send(int id, Object data) { - Object msg = data; - RChannel channel = getChannelFromId(id); - if ((msg instanceof RAbstractVector && !(msg instanceof RList)) || msg instanceof RDataFrame || msg instanceof RFactor) { - // make sure that what's passed through the channel will be copied on the first - // update - RShareable shareable = (RShareable) msg; + private static class SerializedList { + + private RList list; + + public SerializedList(RList list) { + this.list = list; + } + + public RList getList() { + return list; + } + } + + public static void makeShared(Object o) { + if (o instanceof RShareable) { + RShareable shareable = (RShareable) o; if (FastROptions.NewStateTransition) { shareable.incRefCount(); shareable.incRefCount(); } else { shareable.makeShared(); } + } + } + + private static Object convertPrivate(Object o) throws IOException { + if (o instanceof RList) { + RList list = (RList) o; + return createShareable(list); + } else if (!(o instanceof RFunction || o instanceof REnvironment || o instanceof RConnection || o instanceof RLanguage)) { + // TODO: should we make internal values shareable? + return o; + } else { + return RSerialize.serialize(o, false, true, RSerialize.DEFAULT_VERSION, null); + } + } + + @TruffleBoundary + private static Object createShareable(RList list) throws IOException { + RList newList = list; + for (int i = 0; i < list.getLength(); i++) { + Object el = list.getDataAt(i); + Object newEl = convertPrivate(el); + if (el != newEl) { + // conversion happened update element + if (list == newList) { + // create a shallow copy + newList = (RList) list.copy(); + } + newList.updateDataAt(i, newEl, null); + } + } + return list == newList ? list : new SerializedList(newList); + } + + public static void send(int id, Object data) { + Object msg = data; + RChannel channel = getChannelFromId(id); + if (msg instanceof RList) { + try { + msg = createShareable((RList) msg); + } catch (IOException x) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error creating shareable list"); + } + } else if (!(msg instanceof RFunction || msg instanceof REnvironment || msg instanceof RConnection || msg instanceof RLanguage)) { + // make sure that what's passed through the channel will be copied on the first + // update + makeShared(msg); } else { msg = RSerialize.serialize(msg, false, true, RSerialize.DEFAULT_VERSION, null); } @@ -161,11 +218,30 @@ public class RChannel { } } + @TruffleBoundary + private static void unserializeList(RList list) throws IOException { + for (int i = 0; i < list.getLength(); i++) { + Object el = list.getDataAt(i); + if (el instanceof SerializedList) { + RList elList = ((SerializedList) el).getList(); + unserializeList(elList); + list.updateDataAtAsObject(i, elList, null); + } else if (el instanceof byte[]) { + list.updateDataAt(i, RSerialize.unserialize((byte[]) el, null, null), null); + } + } + } + public static Object receive(int id) { RChannel channel = getChannelFromId(id); try { Object msg = (id < 0 ? channel.masterToClient : channel.clientToMaster).take(); - if (msg instanceof byte[]) { + if (msg instanceof SerializedList) { + RList list = ((SerializedList) msg).getList(); + // list is already private (a shallow copy - do the appropriate changes in place) + unserializeList(list); + return list; + } else if (msg instanceof byte[]) { return RSerialize.unserialize((byte[]) msg, null, null); } else { return msg; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java index 4adade483410a4bb69e79bcb20956d4d916afc4e..2f851da27a6ad5b4328c42dbc487924b2a5da2f8 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java @@ -22,14 +22,16 @@ */ package com.oracle.truffle.r.runtime; +import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.*; + import java.util.*; /** * (Abstract) definition of the standard R command line options. The setting of the values from the * environment is handled in some other class. */ -public class RCmdOptions { - public enum Client { +public final class RCmdOptions { + public static enum Client { R { @Override public String usage() { @@ -56,177 +58,98 @@ public class RCmdOptions { public abstract String usage(); } - private static List<Option<?>> optionList = new ArrayList<>(); - public static final Option<Boolean> HELP = newBooleanOption(true, "h", "help", false, "Print short help message and exit"); - public static final Option<Boolean> VERSION = newBooleanOption(true, "version", false, "Print version info and exit"); - public static final Option<String> ENCODING = newStringOption(false, null, "encoding=ENC", null, "Specify encoding to be used for stdin"); - public static final Option<Boolean> RHOME = newBooleanOption(true, null, "RHOME", false, "Print path to R home directory and exit"); - public static final Option<Boolean> SAVE = newBooleanOption(false, null, "save", false, "Do save workspace at the end of the session"); - public static final Option<Boolean> NO_SAVE = newBooleanOption(true, null, "no-save", false, "Don't save it"); - public static final Option<Boolean> NO_ENVIRON = newBooleanOption(false, null, "no-environ", false, "Don't read the site and user environment files"); - public static final Option<Boolean> NO_SITE_FILE = newBooleanOption(false, null, "no-site-file", false, "Don't read the site-wide Rprofile"); - public static final Option<Boolean> NO_INIT_FILE = newBooleanOption(false, null, "no-init-file", false, "Don't read the user R profile"); - public static final Option<Boolean> RESTORE = newBooleanOption(false, null, "restore", true, "Do restore previously saved objects at startup"); - public static final Option<Boolean> NO_RESTORE_DATA = newBooleanOption(false, null, "no-restore-data", false, "Don't restore previously saved objects"); - public static final Option<Boolean> NO_RESTORE_HISTORY = newBooleanOption(false, null, "no-restore-history", false, "Don't restore the R history file"); - public static final Option<Boolean> NO_RESTORE = newBooleanOption(true, null, "no-restore", false, "Don't restore anything"); - public static final Option<Boolean> VANILLA = newBooleanOption(true, null, "vanilla", false, "Combine --no-save, --no-restore, --no-site-file,\n" - + " --no-init-file and --no-environ"); - public static final Option<Boolean> NO_READLINE = newBooleanOption(false, null, "no-readline", false, "Don't use readline for command-line editing"); - public static final Option<String> MAX_PPSIZE = newStringOption(false, null, "max-ppsize", null, "Set max size of protect stack to N"); - public static final Option<Boolean> QUIET = newBooleanOption(true, "q", "quiet", false, "Don't print startup message"); - public static final Option<Boolean> SILENT = newBooleanOption(true, "silent", false, "Same as --quiet"); - public static final Option<Boolean> SLAVE = newBooleanOption(true, "slave", false, "Make R run as quietly as possible"); - public static final Option<Boolean> INTERACTIVE = newBooleanOption(false, "interactive", false, "Force an interactive session"); - public static final Option<Boolean> VERBOSE = newBooleanOption(false, "verbose", false, "Print more information about progress"); - public static final Option<String> DEBUGGER = newStringOption(true, "d", "debugger=NAME", null, "Run R through debugger NAME"); - public static final Option<String> DEBUGGER_ARGS = newStringOption(false, null, "debugger-args=ARGS", null, "Pass ARGS as arguments to the debugger"); - public static final Option<String> GUI = newStringOption(false, "g TYPE", "gui=TYPE", null, "Use TYPE as GUI; possible values are 'X11' (default)\n" + " and 'Tk'."); - public static final Option<String> ARCH = newStringOption(false, null, "arch=NAME", null, "Specify a sub-architecture"); - public static final Option<Boolean> ARGS = newBooleanOption(true, "args", false, "Skip the rest of the command line"); - public static final Option<String> FILE = newStringOption(true, "f FILE", "file=FILE", null, "Take input from 'FILE'"); - public static final Option<List<String>> EXPR = newStringListOption(true, "e EXPR", null, null, "Execute 'EXPR' and exit"); - public static final Option<String> DEFAULT_PACKAGES = newStringOption(Client.RSCRIPT, false, null, "default-packages=list", null, "Where 'list' is a comma-separated set\n" - + " of package names, or 'NULL'"); - - public static Option<Boolean> newBooleanOption(boolean implemented, String name, boolean defaultValue, String help) { - return newBooleanOption(implemented, null, name, defaultValue, help); - } - - public static Option<Boolean> newBooleanOption(boolean implemented, String shortName, String name, boolean defaultValue, String help) { - Option<Boolean> option = new Option<>(implemented, OptionType.BOOLEAN, shortName, name, help, defaultValue); - optionList.add(option); - return option; - } - - public static Option<String> newStringOption(boolean implemented, String shortName, String name, String defaultValue, String help) { - return newStringOption(Client.EITHER, implemented, shortName, name, defaultValue, help); - } - - public static Option<String> newStringOption(Client client, boolean implemented, String shortName, String name, String defaultValue, String help) { - Option<String> option = new Option<>(client, implemented, OptionType.STRING, shortName, name, help, defaultValue); - optionList.add(option); - return option; - } - - public static Option<List<String>> newStringListOption(boolean implemented, String shortName, String name, List<String> defaultValue, String help) { - Option<List<String>> option = new Option<>(Client.EITHER, implemented, OptionType.REPEATED_STRING, shortName, name, help, defaultValue); - optionList.add(option); - return option; - } - - public static List<Option<?>> optionList() { - return optionList; - } - - public static void reset() { - for (Option<?> option : optionList) { - option.reset(); - } - } - - public static enum OptionType { + public static enum RCmdOptionType { BOOLEAN, STRING, REPEATED_STRING } - public static class Option<T> { - public final OptionType type; - /** - * The plain option name as passed to the constructor. - */ - public final String plainName; - /** - * The option name prefixed by {@code --} or {@code null} if no {@code --} form. - */ - private final String name; - /** - * The short option name prefixed by {@code -} or {@code null} if no {@code -} form. - */ + public static enum RCmdOption { + HELP(RCmdOptionType.BOOLEAN, true, "h", "help", false, "Print short help message and exit"), + VERSION(RCmdOptionType.BOOLEAN, true, "version", false, "Print version info and exit"), + ENCODING(RCmdOptionType.STRING, false, "encoding=ENC", null, "Specify encoding to be used for stdin"), + RHOME(RCmdOptionType.BOOLEAN, true, "RHOME", false, "Print path to R home directory and exit"), + SAVE(RCmdOptionType.BOOLEAN, false, "save", false, "Do save workspace at the end of the session"), + NO_SAVE(RCmdOptionType.BOOLEAN, true, "no-save", false, "Don't save it"), + NO_ENVIRON(RCmdOptionType.BOOLEAN, false, "no-environ", false, "Don't read the site and user environment files"), + NO_SITE_FILE(RCmdOptionType.BOOLEAN, false, "no-site-file", false, "Don't read the site-wide Rprofile"), + NO_INIT_FILE(RCmdOptionType.BOOLEAN, false, "no-init-file", false, "Don't read the user R profile"), + RESTORE(RCmdOptionType.BOOLEAN, false, "restore", true, "Do restore previously saved objects at startup"), + NO_RESTORE_DATA(RCmdOptionType.BOOLEAN, false, "no-restore-data", false, "Don't restore previously saved objects"), + NO_RESTORE_HISTORY(RCmdOptionType.BOOLEAN, false, "no-restore-history", false, "Don't restore the R history file"), + NO_RESTORE(RCmdOptionType.BOOLEAN, true, "no-restore", false, "Don't restore anything"), + VANILLA(RCmdOptionType.BOOLEAN, true, "vanilla", false, "Combine --no-save, --no-restore, --no-site-file,\n--no-init-file and --no-environ"), + NO_READLINE(RCmdOptionType.BOOLEAN, false, "no-readline", false, "Don't use readline for command-line editing"), + MAX_PPSIZE(RCmdOptionType.STRING, false, "max-ppsize", null, "Set max size of protect stack to N"), + QUIET(RCmdOptionType.BOOLEAN, true, "q", "quiet", false, "Don't print startup message"), + SILENT(RCmdOptionType.BOOLEAN, true, "silent", false, "Same as --quiet"), + SLAVE(RCmdOptionType.BOOLEAN, true, "slave", false, "Make R run as quietly as possible"), + INTERACTIVE(RCmdOptionType.BOOLEAN, false, "interactive", false, "Force an interactive session"), + VERBOSE(RCmdOptionType.BOOLEAN, false, "verbose", false, "Print more information about progress"), + DEBUGGER(RCmdOptionType.STRING, true, "d", "debugger=NAME", null, "Run R through debugger NAME"), + DEBUGGER_ARGS(RCmdOptionType.STRING, false, "debugger-args=ARGS", null, "Pass ARGS as arguments to the debugger"), + GUI(RCmdOptionType.STRING, false, "g TYPE", "gui=TYPE", null, "Use TYPE as GUI; possible values are 'X11' (default)\nand 'Tk'."), + ARCH(RCmdOptionType.STRING, false, "arch=NAME", null, "Specify a sub-architecture"), + ARGS(RCmdOptionType.BOOLEAN, true, "args", false, "Skip the rest of the command line"), + FILE(RCmdOptionType.STRING, true, "f FILE", "file=FILE", null, "Take input from 'FILE'"), + EXPR(RCmdOptionType.REPEATED_STRING, true, "e EXPR", null, null, "Execute 'EXPR' and exit"), + DEFAULT_PACKAGES(RCmdOptionType.STRING, Client.RSCRIPT, false, null, "default-packages=list", null, "Where 'list' is a comma-separated set\nof package names, or 'NULL'"); + + private final RCmdOptionType type; + @SuppressWarnings("unused") private final Client client; + // Whether this option is actually implemented in FastR + private final boolean implemented; + // The short option name prefixed by {@code -} or {@code null} if no {@code -} form. private final String shortName; - /** - * The '=' separated suffix, e.g. {@code --file=FILE}. - */ - private String suffix; - /** - * The space separated suffix, e.g. {@code -g TYPE}. - */ - private String shortSuffix; - /** - * The help text. - */ - public final String help; - - /** The default value. */ - private final T defaultValue; - /** - * The value, either from the default or set on the command line. - */ - - private T value; - /** - * Set {@code true} iff the short form of a {@code OptionType.STRING} option matched. - */ - private boolean matchedShort; - /** - * Temporary field indicating not implemented. - */ - public final boolean implemented; - - /** - * Option specificity. - */ - public final Client client; - - Option(Client client, boolean implemented, OptionType type, String shortName, String name, String help, T defaultValue) { + // The option name prefixed by {@code --} or {@code null} if no {@code --} form. + private final String name; + // The plain option name as passed to the constructor. + public final String plainName; + // The '=' separated suffix, e.g. {@code --file=FILE}. + private final String suffix; + // The space separated suffix, e.g. {@code -g TYPE}. + private final String shortSuffix; + private final Object defaultValue; + private final String help; + + private RCmdOption(RCmdOptionType type, Client client, boolean implemented, String shortName, String name, Object defaultValue, String help) { + this.type = type; this.client = client; this.implemented = implemented; - this.type = type; - this.shortName = shortName == null ? null : shortKey(shortName); + if (shortName == null) { + this.shortName = null; + this.shortSuffix = null; + } else { + int spx = shortName.indexOf(' '); + this.shortName = "-" + (spx > 0 ? shortName.substring(0, spx) : shortName); + this.shortSuffix = spx > 0 ? shortName.substring(spx) : null; + } this.plainName = name; - this.name = name == null ? null : key(name); - this.help = help; + if (name == null) { + this.name = null; + this.suffix = null; + } else { + int eqx = noPrefix(name) ? -1 : name.indexOf('='); + this.name = "--" + (eqx > 0 ? name.substring(0, eqx) : name); + this.suffix = eqx > 0 ? name.substring(eqx) : null; + } this.defaultValue = defaultValue; - this.value = defaultValue; - } - - Option(boolean implemented, OptionType type, String shortName, String name, String help, T defaultValue) { - this(Client.EITHER, implemented, type, shortName, name, help, defaultValue); - } - - private static boolean noPrefix(String arg) { - return arg.equals("RHOME") || arg.equals("CMD"); + this.help = help.replace("\n", "\n "); } - private String key(String keyName) { - if (noPrefix(keyName)) { - return keyName; - } - String xName = keyName; - int eqx = keyName.indexOf('='); - if (eqx > 0) { - xName = keyName.substring(0, eqx); - suffix = keyName.substring(eqx); - } - return "--" + xName; + private RCmdOption(RCmdOptionType type, boolean implemented, String shortName, String name, Object defaultValue, String help) { + this(type, Client.EITHER, implemented, shortName, name, defaultValue, help); } - private String shortKey(String keyName) { - String xName = keyName; - int spx = keyName.indexOf(' '); - if (spx > 0) { - xName = keyName.substring(0, spx); - shortSuffix = keyName.substring(spx); - } - return "-" + xName; + private RCmdOption(RCmdOptionType type, boolean implemented, String name, Object defaultValue, String help) { + this(type, Client.EITHER, implemented, null, name, defaultValue, help); } - public T getValue() { - return value; + private static boolean noPrefix(String arg) { + return arg.equals("RHOME") || arg.equals("CMD"); } - boolean matches(String arg) { + private boolean matches(String arg) { if (shortName != null && arg.equals(shortName)) { return true; } @@ -236,28 +159,7 @@ public class RCmdOptions { return false; } - @SuppressWarnings("unchecked") - public void setValue(boolean value) { - this.value = (T) new Boolean(value); - } - - public void reset() { - this.value = defaultValue; - } - - @SuppressWarnings("unchecked") - public void setValue(String value) { - if (type == OptionType.REPEATED_STRING) { - if (this.value == null) { - this.value = (T) new ArrayList<String>(); - } - ((List<String>) this.value).add(value); - } else { - this.value = (T) value; - } - } - - public String getHelpName() { + private String getHelpName() { String result = ""; if (shortName != null) { result = shortName; @@ -276,30 +178,207 @@ public class RCmdOptions { } return result; } + } - public boolean matchedShort() { - return matchedShort; + private final EnumMap<RCmdOption, Object> optionValues; + /** + * The original {@code args} array, with element zero set to "FastR". + */ + private String[] arguments; + /** + * Index in {@code args} of the first non-option argument or {@code args.length} if none. + */ + private final int firstNonOptionArgIndex; + + private RCmdOptions(EnumMap<RCmdOption, Object> optionValues, String[] args, int firstNonOptionArgIndex) { + this.optionValues = optionValues; + this.arguments = args; + this.firstNonOptionArgIndex = firstNonOptionArgIndex; + } + + private static void setValue(EnumMap<RCmdOption, Object> optionValues, RCmdOption option, boolean value) { + assert option.type == RCmdOptionType.BOOLEAN; + optionValues.put(option, value); + } + + private static void setValue(EnumMap<RCmdOption, Object> optionValues, RCmdOption option, String value) { + if (option.type == RCmdOptionType.REPEATED_STRING) { + @SuppressWarnings("unchecked") + ArrayList<String> list = (ArrayList<String>) optionValues.get(option); + if (list == null) { + optionValues.put(option, list = new ArrayList<>()); + } + list.add(value); + } else { + assert option.type == RCmdOptionType.STRING; + optionValues.put(option, value); } } - public static Option<?> matchOption(String arg) { - for (Option<?> option : optionList) { - if (option.type == OptionType.BOOLEAN) { + public void setValue(RCmdOption option, boolean value) { + setValue(optionValues, option, value); + } + + public void setValue(RCmdOption option, String value) { + setValue(optionValues, option, value); + } + + public boolean getBoolean(RCmdOption option) { + assert option.type == RCmdOptionType.BOOLEAN; + Object value = optionValues.get(option); + return value == null ? (Boolean) option.defaultValue : (Boolean) value; + } + + public String getString(RCmdOption option) { + assert option.type == RCmdOptionType.STRING; + Object value = optionValues.get(option); + return value == null ? (String) option.defaultValue : (String) value; + } + + @SuppressWarnings("unchecked") + public List<String> getStringList(RCmdOption option) { + assert option.type == RCmdOptionType.REPEATED_STRING; + Object value = optionValues.get(option); + return value == null ? (List<String>) option.defaultValue : (List<String>) value; + } + + private static final class MatchResult { + private final RCmdOption option; + private final boolean matchedShort; + + public MatchResult(RCmdOption option, boolean matchedShort) { + this.option = option; + this.matchedShort = matchedShort; + } + } + + private static MatchResult matchOption(String arg) { + for (RCmdOption option : RCmdOption.values()) { + if (option.type == RCmdOptionType.BOOLEAN) { // these must match exactly if (option.matches(arg)) { - return option; + return new MatchResult(option, false); } - } else if (option.type == OptionType.STRING || option.type == OptionType.REPEATED_STRING) { + } else if (option.type == RCmdOptionType.STRING || option.type == RCmdOptionType.REPEATED_STRING) { // short forms must match exactly (and consume next argument) if (option.shortName != null && option.shortName.equals(arg)) { - option.matchedShort = true; - return option; + return new MatchResult(option, true); } else if (arg.indexOf('=') > 0 && option.name != null && arg.startsWith(option.name)) { - return option; + return new MatchResult(option, false); } } } return null; } + /** + * Parse the arguments from the standard R/Rscript command line syntax, setting the + * corresponding values. + * + * R supports {@code --arg=value} or {@code -arg value} for string-valued options. + * + * The spec for {@code commandArgs()} states that it returns the executable by which R was + * invoked in element 0, which is consistent with the C {@code main} function, but defines the + * exact form to be platform independent. Java does not provide the executable (for obvious + * reasons) so we use "FastR". + */ + public static RCmdOptions parseArguments(Client client, String[] args) { + EnumMap<RCmdOption, Object> options = new EnumMap<>(RCmdOption.class); + int i = 0; + 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) { + firstNonOptionArgIndex = i; + break; + } + // GnuR does not abort, simply issues a warning + System.out.printf("WARNING: unknown option '%s'%n", arg); + i++; + continue; + } else { + RCmdOption option = result.option; + if (result.matchedShort && i == args.length - 1) { + System.out.println("usage:"); + printHelp(client, 1); + } + // check implemented + if (!option.implemented) { + System.out.println("WARNING: option: " + arg + " is not implemented"); + } + if (result.matchedShort) { + i++; + setValue(options, option, args[i]); + } else { + if (option.type == RCmdOptionType.BOOLEAN) { + setValue(options, option, true); + } else if (option.type == RCmdOptionType.STRING) { + int eqx = arg.indexOf('='); + setValue(options, option, arg.substring(eqx + 1)); + } + } + i++; + // check for --args, in which case stop parsing + if (option == RCmdOption.ARGS) { + firstNonOptionArgIndex = i; + break; + } + } + } + String[] xargs = new String[args.length + 1]; + xargs[0] = "FastR"; + System.arraycopy(args, 0, xargs, 1, args.length); + + // adjust for inserted executable name + return new RCmdOptions(options, xargs, firstNonOptionArgIndex + 1); + } + + public String[] getArguments() { + return arguments; + } + + public int getFirstNonOptionArgIndex() { + return firstNonOptionArgIndex; + } + + public void setArguments(String[] arguments) { + this.arguments = arguments; + } + + public void printHelpAndVersion() { + if (getBoolean(HELP)) { + RCmdOptions.printHelp(RCmdOptions.Client.R, 0); + } else if (getBoolean(VERSION)) { + printVersionAndExit(); + } else if (getBoolean(RHOME)) { + printRHomeAndExit(); + } + } + + public static void printHelp(Client client, int exitCode) { + System.out.println(client.usage()); + System.out.println("Options:"); + for (RCmdOption option : RCmdOption.values()) { + System.out.printf(" %-22s %s%n", option.getHelpName(), option.help); + } + System.out.println("\nFILE may contain spaces but not shell metacharacters.\n"); + if (exitCode >= 0) { + System.exit(exitCode); + } + } + + private static void printVersionAndExit() { + System.out.print("FastR version "); + System.out.println(RVersionNumber.FULL); + System.out.println(RRuntime.LICENSE); + Utils.exit(0); + } + + private static void printRHomeAndExit() { + System.out.println(REnvVars.rHome()); + throw Utils.exit(0); + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java deleted file mode 100644 index 0f5618d8337bbeef1b63bb0c684ee4c5f88a9ef3..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RContext.java +++ /dev/null @@ -1,944 +0,0 @@ -/* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.r.runtime; - -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; - -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.vm.*; -import com.oracle.truffle.r.runtime.conn.*; -import com.oracle.truffle.r.runtime.data.*; -import com.oracle.truffle.r.runtime.env.*; -import com.oracle.truffle.r.runtime.env.REnvironment.*; -import com.oracle.truffle.r.runtime.ffi.*; -import com.oracle.truffle.r.runtime.rng.*; - -/** - * Encapsulates the runtime state ("context") of an R session. All access to that state from the - * implementation <b>must</b> go through this class. There can be multiple instances - * (multiple-tenancy) active within a single process/Java-VM. - * - * The context provides two sub-interfaces {@link ConsoleHandler} and {@link Engine}that are - * (typically) implemented elsewhere, and accessed through {@link #getConsoleHandler()} and - * {@link #getEngine()}, respectively. - * - * Context-specific state for implementation classes is managed by this class (or the associated - * engine) and accessed through the {@code getXXXState} methods. - * - * The life-cycle of a {@link RContext} is: - * <ol> - * <li>created: {@link #createShareNothing(RContext, String[], ConsoleHandler)} or - * {@link #createShareParentReadOnly(RContext, String[], ConsoleHandler)}</li> - * <li>activated: {@link #activate()}</li> - * <li>destroyed: {@link #destroy()}</li> - * </ol> - * - * Evaluations are only possible on an active context. - * - */ -public final class RContext extends ExecutionContext { - - public static final int CONSOLE_WIDTH = 80; - - /** - * The interface to a source of input/output for the context, which may have different - * implementations for different contexts. Since I/O is involved, all methods are tagged with - * {@link TruffleBoundary} as a hint that so should the associated implementation methods. - */ - public interface ConsoleHandler { - /** - * Normal output with a new line. - */ - @TruffleBoundary - void println(String s); - - /** - * Normal output without a newline. - */ - @TruffleBoundary - void print(String s); - - /** - * Formatted output. - */ - @TruffleBoundary - default void printf(String format, Object... args) { - print(String.format(format, args)); - } - - /** - * Error output with a newline. - * - * @param s - */ - @TruffleBoundary - void printErrorln(String s); - - /** - * Error output without a newline. - */ - @TruffleBoundary - void printError(String s); - - /** - * Read a line of input, newline included in result. Returns null if - * {@link #isInteractive() == false}. The rationale for including the readline is to ensure - * that the accumulated input, whether it be from a file or the console accurately reflects - * the the source. TODO worry about "\r\n"? - */ - @TruffleBoundary - String readLine(); - - /** - * Return {@code true} if and only if this console is interactive. - */ - @TruffleBoundary - boolean isInteractive(); - - /** - * Redirect error output to the normal output. - */ - @TruffleBoundary - void redirectError(); - - /** - * Get the current prompt. - */ - @TruffleBoundary - String getPrompt(); - - /** - * Set the R prompt. - */ - @TruffleBoundary - void setPrompt(String prompt); - - /** - * Get the console width. - */ - @TruffleBoundary - int getWidth(); - } - - public enum Kind { - /** - * Essentially a clean restart, modulo the basic VM-wide initialization. which does include, - * for example, reading the external environment variables. I.e., it is not a goal to create - * a multi-user environment with different external inputs. This kind of context can be used - * in a parallel computation. The initial context is always of this kind. The intent is that - * all mutable state is localized to the context, which may not be completely achievable. - * For example, shared native libraries are assumed to contain no mutable state and be - * re-entrant. - */ - SHARE_NOTHING, - - /** - * Shares the set of loaded packages of the parent context at the time the context is - * created. Only useful when there is a priori knowledge on the evaluation that the context - * will be used for. Cannot safely be used for parallel context evaluation. Must be created - * as a child of an existing parent context of type {@link #SHARE_NOTHING} or - * {@link #SHARE_PARENT_RO} and only one such child is allowed. (Strictly speaking the - * invariant should be only one active child, but the implementation enforces it at creation - * time). Evidently any changes made to the shared environment, e.g., loading a package, - * affect the parent. - */ - SHARE_PARENT_RW, - - /** - * Intermediate between {@link #SHARE_NOTHING} and {@link #SHARE_PARENT_RW}, this is similar - * to the standard shared code/copied data model provided by operating systems, although the - * code/data distinction isn't completely applicable to a language like R. Unlike - * {@link #SHARE_NOTHING}, where the ASTs for the functions in the default packages are - * distinct copies in each context, in this kind of context, they are shared. Strictly - * speaking, the bindings of R functions are shared, and this is achieved by creating a - * shallow copy of the environments associated with the default packages of the parent - * context at the time the context is created. - */ - SHARE_PARENT_RO, - } - - /** - * Denotes a class that has context-specific state, e.g. {@link REnvironment}. Such a class must - * implement the {@link #newContext} method. - */ - public interface StateFactory { - /** - * Create the class-specific state for a new context. - * - * @param context the context - * @param objects additional arguments for custom initialization, typically empty - */ - ContextState newContext(RContext context, Object... objects); - - /** - * A state factory may want to snapshot the state of the context just after the basic system - * is initialized, in which case they can override this method. N.B. This is only invoked - * for {@link Kind#SHARE_NOTHING} contexts. The definition of "system initialized" is that - * the default packages have been loaded, profiles evaluated and {@code .First, First.Sys} - * executed. - */ - @SuppressWarnings("unused") - default void systemInitialized(RContext context, ContextState state) { - - } - - /** - * Called in response to the {@link RContext#destroy} method. Provides a hook for finalizing - * any state before the context is destroyed. - */ - @SuppressWarnings("unused") - default void beforeDestroy(RContext context, ContextState state) { - - } - } - - /** - * Tagging interface denoting a class that carries the context-specific state for a class that - * implements {@link StateFactory}. The class specific state must implement this interface. - */ - public interface ContextState { - - } - - public interface Engine { - /** - * Make the engine ready for evaluations. - */ - void activate(); - - /** - * Return the {@link TruffleVM} instance associated with this engine. - */ - TruffleVM getTruffleVM(); - - /** - * Return the {@link com.oracle.truffle.api.vm.TruffleVM.Builder} instance associated with - * this engine. This is only used by the command line debugger. - */ - TruffleVM.Builder getTruffleVMBuilder(); - - /** - * Elapsed time of runtime. - * - * @return elapsed time in nanosecs. - */ - long elapsedTimeInNanos(); - - /** - * Return user and system times for any spawned child processes in nanosecs, < 0 means not - * available (Windows). - */ - long[] childTimesInNanos(); - - public static class ParseException extends Exception { - private static final long serialVersionUID = 1L; - - public ParseException(Throwable cause, String msg) { - super(msg, cause); - } - } - - /** - * Parse an R expression and return an {@link RExpression} object representing the Truffle - * ASTs for the components. - */ - RExpression parse(Source source) throws ParseException; - - /** - * A (perhaps temporary) interface to support {@link TruffleLanguage}. - */ - CallTarget parseToCallTarget(Source source); - - /** - * Parse and evaluate {@code rscript} in {@code frame}. {@code printResult == true}, the - * result of the evaluation is printed to the console. - * - * @param sourceDesc a {@link Source} object that describes the input to be parsed - * @param frame the frame in which to evaluate the input - * @param printResult {@code true} iff the result of the evaluation should be printed to the - * console - * @param allowIncompleteSource {@code true} if partial input is acceptable - * @return the object returned by the evaluation or {@code null} if an error occurred. - */ - Object parseAndEval(Source sourceDesc, MaterializedFrame frame, boolean printResult, boolean allowIncompleteSource); - - /** - * Variant of {@link #parseAndEval(Source, MaterializedFrame, boolean, boolean)} for - * evaluation in the global frame. - */ - Object parseAndEval(Source sourceDesc, boolean printResult, boolean allowIncompleteSource); - - /** - * Variant of {@link #parseAndEval(Source, MaterializedFrame, boolean, boolean)} for test - * evaluation in the global frame. - */ - Object parseAndEvalTest(Source sourceDesc, boolean printResult, boolean allowIncompleteSource); - - Object INCOMPLETE_SOURCE = new Object(); - - /** - * Support for the {@code eval} {@code .Internal}. - */ - Object eval(RExpression expr, REnvironment envir, REnvironment enclos, int depth) throws PutException; - - /** - * Convenience method for common case. - */ - default Object eval(RExpression expr, REnvironment envir, int depth) throws PutException { - return eval(expr, envir, null, depth); - } - - /** - * Variant of {@link #eval(RExpression, REnvironment, REnvironment, int)} for a single - * language element. - */ - Object eval(RLanguage expr, REnvironment envir, REnvironment enclos, int depth) throws PutException; - - /** - * Convenience method for common case. - */ - default Object eval(RLanguage expr, REnvironment envir, int depth) throws PutException { - return eval(expr, envir, null, depth); - } - - /** - * Evaluate {@code expr} in {@code frame}. - */ - Object eval(RExpression expr, MaterializedFrame frame); - - /** - * Variant of {@link #eval(RExpression, MaterializedFrame)} for a single language element. - */ - Object eval(RLanguage expr, MaterializedFrame frame); - - /** - * Variant of {@link #eval(RLanguage, MaterializedFrame)} where we already have the - * {@link RFunction} and the evaluated arguments, but do not have a frame available, and we - * are behind a {@link TruffleBoundary}, so call inlining is not an issue. This is primarily - * used for R callbacks from {@link RErrorHandling} and {@link RSerialize}. - */ - Object evalFunction(RFunction func, Object... args); - - /** - * Evaluates an {@link com.oracle.truffle.r.runtime.data.RPromise.Closure} in {@code frame}. - */ - Object evalPromise(RPromise.Closure closure, MaterializedFrame frame); - - /** - * Checks for the existence of {@code .Last/.Last.sys} and if present and bound to a - * function, invokes the (parameterless) function. - */ - void checkAndRunLast(String name); - - /** - * Wraps the Truffle AST in {@code body} in an anonymous function and returns a - * {@link RootCallTarget} for it. - * - * N.B. For certain expressions, there might be some value in enclosing the wrapper function - * in a specific lexical scope. E.g., as a way to access names in the expression known to be - * defined in that scope. - * - * @param body The AST for the body of the wrapper, i.e., the expression being evaluated. - */ - RootCallTarget makePromiseCallTarget(Object body, String funName); - - /** - * Used by Truffle debugger; invokes the internal "print" support in R for {@code value}. - * Essentially this is equivalent to {@link #evalFunction} using the {@code "print"} - * function. - */ - void printResult(Object value); - - RFunction parseFunction(String name, Source source, MaterializedFrame enclosingFrame) throws ParseException; - - } - - /** - * The set of classes for which the context manages context-specific state, and their state. We - * could do this more dynamically with a registration process, perhaps driven by an annotation - * processor, but the set is relatively small, so we just enumerate them here. - */ - public static enum ClassStateKind { - ROptions(ROptions.class, false), - REnvironment(REnvironment.ClassStateFactory.class, true), - RErrorHandling(RErrorHandling.class, false), - RConnection(ConnectionSupport.class, false), - StdConnections(StdConnections.class, true), - RNG(RRNG.class, false), - RFFI(RFFIContextStateFactory.class, false), - RSerialize(RSerialize.class, false), - REnvVars(REnvVars.class, false); - - private final Class<? extends StateFactory> klass; - private StateFactory factory; - private final boolean customCreate; - - private ClassStateKind(Class<? extends StateFactory> klass, boolean customCreate) { - this.klass = klass; - this.customCreate = customCreate; - } - - private static final ClassStateKind[] VALUES = values(); - // Avoid reflection at runtime (AOT VM). - static { - for (ClassStateKind css : VALUES) { - try { - css.factory = css.klass.newInstance(); - } catch (IllegalAccessException | InstantiationException ex) { - throw Utils.fail("failed to instantiate ClassStateFactory: " + css.klass.getSimpleName()); - } - } - } - - } - - /** - * A thread that is explicitly associated with a context for efficient lookup. - */ - public static class ContextThread extends Thread { - protected RContext context; - - public ContextThread(RContext context) { - this.context = context; - } - - protected ContextThread() { - - } - - public void setContext(RContext context) { - this.context = context; - } - - } - - /** - * A thread for performing an evaluation (used by {@code fastr} package. - */ - public static class EvalThread extends ContextThread { - private final Source source; - - public EvalThread(RContext context, Source source) { - super(context); - this.source = source; - context.evalThread = this; - } - - @Override - public void run() { - try { - context.activate(); - context.engine.parseAndEval(source, true, false); - } finally { - context.destroy(); - } - } - - } - - /** - * Builtin cache. Valid across all contexts. - */ - private static final HashMap<Object, RFunction> cachedBuiltinFunctions = new HashMap<>(); - - private final Kind kind; - - private final GlobalAssumptions globalAssumptions = new GlobalAssumptions(); - - /** - * Denote whether the result of an expression should be printed in the shell or not. - */ - private boolean resultVisible = true; - - /** - * A context-specific value that is checked in {@code HiddenInternalFunctions} to avoid an error - * report on a {@code SUBSTITUTE} builtin. Not worth promoting to a {@link ContextState}. - */ - private boolean loadingBase; - - /** - * Denote whether the FastR instance is running in 'interactive' mode. This can be set in a - * number of ways and is <b>not</> simply equivalent to taking input from a file. However, it is - * final once set. - */ - @CompilationFinal private boolean interactive; - - @CompilationFinal private ConsoleHandler consoleHandler; - @CompilationFinal private String[] commandArgs; - @CompilationFinal private Engine engine; - - /** - * The array is indexed by {@link ClassStateKind#ordinal()}. - */ - @CompilationFinal private ContextState[] contextState; - - /** - * Any context created by another has a parent. When such a context is destroyed we must reset - * the {@link #threadLocalContext} to the parent. - */ - private final RContext parent; - - /** - * At most one shared child. - */ - private RContext sharedChild; - - /** - * Back pointer to the evalThread. - */ - private EvalThread evalThread; - - /** - * Typically there is a 1-1 relationship between an {@link RContext} and the thread that is - * performing the evaluation, so we can store the {@link RContext} in a {@link ThreadLocal}. - * - * When a context is first created no threads are attached, to allow contexts to be used as - * values in the experimental {@code fastr.createcontext} function. The {@link #engine} must - * call the {@link #activate} method once the context becomes active. Additional threads can be - * added by the {@link #attachThread} method. - */ - @CompilationFinal private static final ThreadLocal<RContext> threadLocalContext = new ThreadLocal<>(); - - /** - * Used by the MethodListDispatch class. - */ - - private boolean methodTableDispatchOn = true; - - /* - * Primarily for debugging. - */ - private static final AtomicLong ID = new AtomicLong(); - @CompilationFinal private long id; - private boolean active; - - private static final Deque<RContext> allContexts = new ConcurrentLinkedDeque<>(); - - private static final Semaphore allContextsSemaphore = new Semaphore(1, true); - - /** - * A (hopefully) temporary workaround to ignore the setting of {@link #resultVisible} for - * benchmarks. Set across all contexts. - */ - @CompilationFinal private static boolean ignoreVisibility; - - /* - * Workarounds to finesse project circularities between runtime/nodes. - */ - @CompilationFinal private static RRuntimeASTAccess runtimeASTAccess; - @CompilationFinal private static RBuiltinLookup builtinLookup; - - /** - * Initialize VM-wide static values. - */ - public static void initialize(RRuntimeASTAccess rASTHelperArg, RBuiltinLookup rBuiltinLookupArg, boolean ignoreVisibilityArg) { - runtimeASTAccess = rASTHelperArg; - builtinLookup = rBuiltinLookupArg; - ignoreVisibility = ignoreVisibilityArg; - } - - /** - * Associates this {@link RContext} with the current thread. - */ - public void attachThread() { - Thread current = Thread.currentThread(); - if (current instanceof ContextThread) { - ((ContextThread) current).setContext(this); - } else { - threadLocalContext.set(this); - } - } - - /** - * Waits for the associated EvalThread to finish. - * - * @throws InterruptedException - */ - public void joinThread() throws InterruptedException { - EvalThread t = this.evalThread; - if (t == null) { - throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "no eval thread in a given context"); - } - this.evalThread = null; - t.join(); - } - - private static final Assumption singleContextAssumption = Truffle.getRuntime().createAssumption("single RContext"); - @CompilationFinal private static RContext singleContext; - - private RContext(Kind kind, RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) { - if (kind == Kind.SHARE_PARENT_RW) { - if (parent.sharedChild != null) { - throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "can't have multiple active SHARED_PARENT_RW contexts"); - } - parent.sharedChild = this; - } - this.kind = kind; - this.parent = parent; - this.id = ID.getAndIncrement(); - this.commandArgs = commandArgs; - if (consoleHandler == null) { - throw Utils.fail("no console handler set"); - } - this.consoleHandler = consoleHandler; - this.interactive = consoleHandler.isInteractive(); - try { - allContextsSemaphore.acquire(); - allContexts.add(this); - allContextsSemaphore.release(); - } catch (InterruptedException x) { - throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context"); - } - - if (singleContextAssumption.isValid()) { - if (singleContext == null) { - singleContext = this; - } else { - singleContext = null; - singleContextAssumption.invalidate(); - } - } - } - - public void installCustomClassState(ClassStateKind classStateKind, ContextState state) { - assert classStateKind.customCreate; - assert contextState[classStateKind.ordinal()] == null; - contextState[classStateKind.ordinal()] = state; - } - - /** - * Inform state factories that the system is initialized. - */ - public void systemInitialized() { - for (ClassStateKind classStateKind : ClassStateKind.VALUES) { - classStateKind.factory.systemInitialized(this, contextState[classStateKind.ordinal()]); - } - } - - /** - * Create a {@link Kind#SHARE_NOTHING} {@link RContext}. - * - * @param parent if non-null {@code null} the parent creating the context - * @param commandArgs the command line arguments passed this R session - * @param consoleHandler a {@link ConsoleHandler} for output - */ - public static RContext createShareNothing(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) { - RContext result = create(parent, Kind.SHARE_NOTHING, commandArgs, consoleHandler); - return result; - } - - /** - * Create a {@link Kind#SHARE_PARENT_RO} {@link RContext}. - * - * @param parent parent context with which to shgre - * @param commandArgs the command line arguments passed this R session - * @param consoleHandler a {@link ConsoleHandler} for output - */ - public static RContext createShareParentReadOnly(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) { - RContext result = create(parent, Kind.SHARE_PARENT_RO, commandArgs, consoleHandler); - return result; - - } - - /** - * Create a {@link Kind#SHARE_PARENT_RW} {@link RContext}. - * - * @param parent parent context with which to shgre - * @param commandArgs the command line arguments passed this R session - * @param consoleHandler a {@link ConsoleHandler} for output - */ - public static RContext createShareParentReadWrite(RContext parent, String[] commandArgs, ConsoleHandler consoleHandler) { - RContext result = create(parent, Kind.SHARE_PARENT_RW, commandArgs, consoleHandler); - return result; - - } - - /** - * Create a context of a given kind. - */ - public static RContext create(RContext parent, Kind kind, String[] commandArgs, ConsoleHandler consoleHandler) { - RContext result = new RContext(kind, parent, commandArgs, consoleHandler); - result.engine = RContext.getRRuntimeASTAccess().createEngine(result); - return result; - } - - /** - * Active the context by attaching the current thread and initializing the {@link StateFactory} - * objects. Note that we attach the thread before creating the new context state. This means - * that code that accesses the state through this interface will receive a {@code null} value. - * Access to the parent state is available through the {@link RContext} argument passed to the - * {@link StateFactory#newContext(RContext, Object...)} method. It might be better to attach the - * thread after state creation but it is a finely balanced decision and risks incorrectly - * accessing the parent state. - */ - public RContext activate() { - assert !active; - active = true; - attachThread(); - contextState = new ContextState[ClassStateKind.VALUES.length]; - for (ClassStateKind classStateKind : ClassStateKind.VALUES) { - if (!classStateKind.customCreate) { - contextState[classStateKind.ordinal()] = classStateKind.factory.newContext(this); - } - } - installCustomClassState(ClassStateKind.StdConnections, new StdConnections().newContext(this, consoleHandler)); - if (kind == Kind.SHARE_PARENT_RW) { - parent.sharedChild = this; - } - // The environment state installation is handled by the engine - engine.activate(); - return this; - } - - /** - * Destroy this context. - */ - public void destroy() { - for (ClassStateKind classStateKind : ClassStateKind.VALUES) { - classStateKind.factory.beforeDestroy(this, contextState[classStateKind.ordinal()]); - } - if (kind == Kind.SHARE_PARENT_RW) { - parent.sharedChild = null; - } - engine = null; - try { - allContextsSemaphore.acquire(); - allContexts.remove(this); - allContextsSemaphore.release(); - } catch (InterruptedException x) { - throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context"); - } - if (parent == null) { - threadLocalContext.set(null); - } else { - threadLocalContext.set(parent); - } - } - - public RContext getParent() { - return parent; - } - - public Kind getKind() { - return kind; - } - - public long getId() { - return id; - } - - public static RContext find(int id) { - try { - allContextsSemaphore.acquire(); - for (RContext context : allContexts) { - if (context.id == id) { - allContextsSemaphore.release(); - return context; - } - } - allContextsSemaphore.release(); - } catch (InterruptedException x) { - throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "error destroying context"); - } - return null; - } - - public GlobalAssumptions getAssumptions() { - return globalAssumptions; - } - - @TruffleBoundary - private static RContext getInstanceInternal() { - RContext result = threadLocalContext.get(); - assert result != null; - assert result.active; - return result; - } - - public static RContext getInstance() { - RContext context = singleContext; - if (context != null) { - try { - singleContextAssumption.check(); - return context; - } catch (InvalidAssumptionException e) { - // fallback to slow case - } - } - - Thread current = Thread.currentThread(); - if (current instanceof ContextThread) { - context = ((ContextThread) current).context; - assert context != null; - return context; - } else { - return getInstanceInternal(); - } - - } - - /** - * Access to the engine, when an {@link RContext} object is available, and/or when {@code this} - * context is not active. - */ - public Engine getThisEngine() { - return engine; - } - - /** - * Access to the {@link ContextState}, when an {@link RContext} object is available, and/or when - * {@code this} context is not active. - */ - public ContextState getThisContextState(ClassStateKind classStateKind) { - return contextState[classStateKind.ordinal()]; - } - - public boolean isVisible() { - return resultVisible; - } - - public void setVisible(boolean v) { - resultVisible = v; - } - - public boolean isMethodTableDispatchOn() { - return methodTableDispatchOn; - } - - public void setMethodTableDispatchOn(boolean on) { - methodTableDispatchOn = on; - } - - public boolean isInteractive() { - return interactive; - } - - public static boolean isIgnoringVisibility() { - return ignoreVisibility; - } - - public ConsoleHandler getConsoleHandler() { - return consoleHandler; - } - - private TimeZone timeZone = TimeZone.getDefault(); - - public TimeZone getSystemTimeZone() { - return timeZone; - } - - public void setSystemTimeZone(TimeZone timeZone) { - this.timeZone = timeZone; - } - - /** - * This is a static property of the implementation and not context-specific. - */ - public static RRuntimeASTAccess getRRuntimeASTAccess() { - return runtimeASTAccess; - } - - /** - * Is {@code name} a builtin function (but not a {@link RBuiltinKind#INTERNAL}? - */ - public static boolean isPrimitiveBuiltin(String name) { - return builtinLookup.isPrimitiveBuiltin(name); - } - - /** - * Return the {@link RFunction} for the builtin {@code name}. - */ - public static RFunction lookupBuiltin(String name) { - return builtinLookup.lookupBuiltin(name); - } - - /** - * Returns the descriptor for the builtin with the given name. This does not cause an RFunction - * to be created. - */ - public static RBuiltinDescriptor lookupBuiltinDescriptor(String name) { - return builtinLookup.lookupBuiltinDescriptor(name); - } - - public static RFunction cacheBuiltin(Object key, RFunction function) { - cachedBuiltinFunctions.put(key, function); - return function; - } - - public static RFunction getCachedBuiltin(Object key) { - return cachedBuiltinFunctions.get(key); - } - - public String[] getCommandArgs() { - if (commandArgs == null) { - throw Utils.fail("no command args set"); - } - return commandArgs; - } - - @Override - public String toString() { - return "context: " + id; - } - - /* - * static functions necessary in code where the context is only implicit in the thread(s) - * running an evaluation - */ - - public static Engine getEngine() { - return RContext.getInstance().engine; - } - - public static ContextState getContextState(ClassStateKind kind) { - return getInstance().contextState[kind.ordinal()]; - } - - public static REnvironment.ContextState getREnvironmentState() { - return (REnvironment.ContextState) getInstance().contextState[ClassStateKind.REnvironment.ordinal()]; - } - - public static ROptions.ContextState getROptionsState() { - return (ROptions.ContextState) getInstance().contextState[ClassStateKind.ROptions.ordinal()]; - } - - public static ConnectionSupport.ContextState getRConnectionState() { - return (ConnectionSupport.ContextState) getInstance().contextState[ClassStateKind.RConnection.ordinal()]; - } - - public void setLoadingBase(boolean b) { - loadingBase = b; - } - - public boolean getLoadingBase() { - return loadingBase; - } - -} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java index e733356efb021a8f0b9f05229fe241e45c88a9de..3b0c10587df95c69e373b618bf9aea80c9bd95c5 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java @@ -17,6 +17,7 @@ import java.util.*; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.source.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute; import com.oracle.truffle.r.runtime.gnur.*; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java index 50360274dae4ffae497f5f130c4d88537dc087fa..639567f9d1f71994c2169496a674864023cec750 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java @@ -27,79 +27,38 @@ import java.nio.file.*; import java.nio.file.FileSystem; import java.util.*; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.ffi.*; /** * Repository for environment variables, including those set by FastR itself, e.g. * {@code R_LIBS_USER}. - * - * Environment variables are context specific and, certainly there is one case in package loading - * where R uses an environment variable as a global variable to detect recursion. - * - * On startup, before we have any contexts created, we have to support access to the environment - * variables inherited from the OS environment, e.g., for {@code R_PROFILE}. Additional variable are - * set during the package loading and these are captured in {@link #systemInitEnvVars}. All - * subsequent contexts inherit that set and may modify it further. */ -public class REnvVars implements RContext.StateFactory { - - private static Map<String, String> initialEnvVars; - private static HashMap<String, String> systemInitEnvVars; - @CompilationFinal private static boolean initialized; - - private static class ContextStateImpl implements RContext.ContextState { - private Map<String, String> envVars = new HashMap<>(); - - } - - public RContext.ContextState newContext(RContext context, Object... objects) { - ContextStateImpl result = new ContextStateImpl(); - result.envVars.putAll(systemInitEnvVars == null ? initialEnvVars : systemInitEnvVars); - if (!initialized) { - initialized = true; - } - return result; - } - - @Override - public void systemInitialized(RContext context, RContext.ContextState state) { - ContextStateImpl optionsState = (ContextStateImpl) state; - systemInitEnvVars = new HashMap<>(optionsState.envVars.size()); - systemInitEnvVars.putAll(optionsState.envVars); - } - - private static ContextStateImpl getState() { - return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.REnvVars); - } +public final class REnvVars implements RContext.ContextState { - private static Map<String, String> getInitialEnvVars() { - if (initialEnvVars == null) { - initialEnvVars = new HashMap<>(System.getenv()); - } - return initialEnvVars; - } + private final Map<String, String> envVars = new HashMap<>(System.getenv()); - private static Map<String, String> getEnvVars() { - if (initialized) { - return getState().envVars; - } else { - return initialEnvVars; - } - } - - public static void initialize() { - getInitialEnvVars(); + private REnvVars(RContext context) { // set the standard vars defined by R String rHome = rHome(); - initialEnvVars.put("R_HOME", rHome); + + // Check any external setting is consistent + String envRHomePath = envVars.get("R_HOME"); + if (envRHomePath != null) { + new File(envRHomePath).getAbsolutePath(); + if (!envRHomePath.equals(rHomePath)) { + Utils.fail("R_HOME set to unexpected value in the environment"); + } + } + envVars.put("R_HOME", rHome); // Always read the system file FileSystem fileSystem = FileSystems.getDefault(); safeReadEnvironFile(fileSystem.getPath(rHome, "etc", "Renviron").toString()); - getEnvVars().put("R_DOC_DIR", fileSystem.getPath(rHome, "doc").toString()); - getEnvVars().put("R_INCLUDE_DIR", fileSystem.getPath(rHome, "include").toString()); - getEnvVars().put("R_SHARE_DIR", fileSystem.getPath(rHome, "share").toString()); + envVars.put("R_DOC_DIR", fileSystem.getPath(rHome, "doc").toString()); + envVars.put("R_INCLUDE_DIR", fileSystem.getPath(rHome, "include").toString()); + envVars.put("R_SHARE_DIR", fileSystem.getPath(rHome, "share").toString()); String rLibsUserProperty = System.getenv("R_LIBS_USER"); if (rLibsUserProperty == null) { String os = System.getProperty("os.name"); @@ -108,19 +67,19 @@ public class REnvVars implements RContext.StateFactory { } else { rLibsUserProperty = "~/R/%p-library/%v"; } - getEnvVars().put("R_LIBS_USER", rLibsUserProperty); + envVars.put("R_LIBS_USER", rLibsUserProperty); // This gets expanded by R code in the system profile } - if (!RCmdOptions.NO_ENVIRON.getValue()) { - String siteFile = getEnvVars().get("R_ENVIRON"); + if (!context.getOptions().getBoolean(RCmdOption.NO_ENVIRON)) { + String siteFile = envVars.get("R_ENVIRON"); if (siteFile == null) { siteFile = fileSystem.getPath(rHome, "etc", "Renviron.site").toString(); } if (new File(siteFile).exists()) { safeReadEnvironFile(siteFile); } - String userFile = getEnvVars().get("R_ENVIRON_USER"); + String userFile = envVars.get("R_ENVIRON_USER"); if (userFile == null) { String dotRenviron = ".Renviron"; userFile = fileSystem.getPath(RFFIFactory.getRFFI().getBaseRFFI().getwd(), dotRenviron).toString(); @@ -149,13 +108,13 @@ public class REnvVars implements RContext.StateFactory { } } - private static String getEitherCase(String var) { - String val = getEnvVars().get(var); - if (val != null) { - return val; - } else { - return getEnvVars().get(var.toUpperCase()); - } + public static REnvVars newContext(RContext context) { + return new REnvVars(context); + } + + private String getEitherCase(String var) { + String val = envVars.get(var); + return val != null ? val : envVars.get(var.toUpperCase()); } private static String rHomePath; @@ -182,38 +141,30 @@ public class REnvVars implements RContext.StateFactory { Utils.fail("cannot find a valid R_HOME"); } } - // Check any external setting is consistent - String envRHomePath = getInitialEnvVars().get("R_HOME"); - if (envRHomePath != null) { - new File(envRHomePath).getAbsolutePath(); - if (!envRHomePath.equals(rHomePath)) { - Utils.fail("R_HOME set to unexpected value in the environment"); - } - } } return rHomePath; } - public static String put(String key, String value) { + public String put(String key, String value) { // TODO need to set value for sub-processes - return getEnvVars().put(key, value); + return envVars.put(key, value); } - public static String get(String key) { - return getEnvVars().get(key); + public String get(String key) { + return envVars.get(key); } - public static boolean unset(String key) { + public boolean unset(String key) { // TODO remove at the system level - getEnvVars().remove(key); + envVars.remove(key); return true; } - public static Map<String, String> getMap() { - return getEnvVars(); + public Map<String, String> getMap() { + return envVars; } - public static void readEnvironFile(String path) throws IOException { + public void readEnvironFile(String path) throws IOException { try (BufferedReader r = new BufferedReader(new FileReader(path))) { String line = null; while ((line = r.readLine()) != null) { @@ -228,12 +179,12 @@ public class REnvVars implements RContext.StateFactory { String var = line.substring(0, ix); String value = expandParameters(line.substring(ix + 1)).trim(); // GnuR does not seem to remove quotes, although the spec says it should - getEnvVars().put(var, value); + envVars.put(var, value); } } } - protected static String expandParameters(String value) { + protected String expandParameters(String value) { StringBuffer result = new StringBuffer(); int x = 0; int paramStart = value.indexOf("${", x); @@ -248,7 +199,7 @@ public class REnvVars implements RContext.StateFactory { paramName = param.substring(0, dx); paramDefault = expandParameters(param.substring(dx + 1)); } - String paramValue = getEnvVars().get(paramName); + String paramValue = envVars.get(paramName); if (paramValue == null || paramValue.length() == 0) { paramValue = paramDefault; } @@ -265,7 +216,7 @@ public class REnvVars implements RContext.StateFactory { throw new IOException(" File " + path + " contains invalid line(s)\n " + line + "\n They were ignored\n"); } - public static void safeReadEnvironFile(String path) { + public void safeReadEnvironFile(String path) { try { readEnvironFile(path); } catch (IOException ex) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index fbb0eaf27202e81a1611288722015f46ed3083a1..cdd1a9f02a81e748ad831e0636cf87e6e36fcb0b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -114,11 +114,32 @@ public final class RError extends RuntimeException { throw error0(node, msg, args); } + @TruffleBoundary + public static RError error(Node node, Message msg, Object... args) { + throw error0(findParentRBase(node), msg, args); + } + @TruffleBoundary public static RError error(RBaseNode node, Message msg) { throw error0(node, msg, (Object[]) null); } + @TruffleBoundary + public static RError error(Node node, Message msg) { + throw error0(findParentRBase(node), msg, (Object[]) null); + } + + private static RBaseNode findParentRBase(Node node) { + Node current = node; + while (current != null) { + if (current instanceof RBaseNode) { + return (RBaseNode) current; + } + current = current.getParent(); + } + throw new AssertionError("Could not find RBaseNode for given Node. Is it not adopted in the AST?"); + } + /** * Handles an R error with the most general argument signature. All other facade variants * delegate to this method. @@ -177,6 +198,12 @@ public final class RError extends RuntimeException { RErrorHandling.warningcall(true, node, msg, args); } + @TruffleBoundary + public static void warning(Node node, Message msg, Object... args) { + assert node != null; + warning(findParentRBase(node), msg, args); + } + @TruffleBoundary public static RError stop(boolean showCall, RBaseNode node, Message msg, Object arg) { assert node != null; @@ -186,7 +213,7 @@ public final class RError extends RuntimeException { @TruffleBoundary public static void performanceWarning(String string) { - if (FastROptions.PerformanceWarnings.getValue()) { + if (FastROptions.PerformanceWarnings) { warning(RError.NO_NODE, Message.PERFORMANCE, string); } } @@ -545,6 +572,7 @@ public final class RError extends RuntimeException { CHAR_VEC_ARGUMENT("a character vector argument expected"), QUOTE_G_ONE("only the first character of 'quote' will be used"), UNEXPECTED("unexpected '%s' in \"%s\""), + UNEXPECTED_LINE("unexpected '%s' in \"%s\" (line %d)"), FIRST_ELEMENT_USED("first element used of '%s' argument"), MUST_BE_COERCIBLE_INTEGER("argument must be coercible to non-negative integer"), DEFAULT_METHOD_NOT_IMPLEMENTED_FOR_TYPE("default method not implemented for type '%s'"), @@ -591,7 +619,10 @@ public final class RError extends RuntimeException { BAD_RESTART("bad restart"), RESTART_NOT_ON_STACK("restart not on stack"), PERFORMANCE("performance problem: %s"), - MUST_BE_SMALL_INT("argument '%s' must be a small integer"); + MUST_BE_SMALL_INT("argument '%s' must be a small integer"), + NO_INTEROP("'%s' is not an object that supports interoperability (class %s)"), + NO_IMPORT_OBJECT("'%s' is not an exported object"), + NO_FUNCTION_RETURN("no function to return from, jumping to top level"); public final String message; final boolean hasArgs; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java index 8e19885192e0c3f52aaeadb3f7c5e0fbf9952c43..156025ae232fb3b8143914486df5556befe4f1f2 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java @@ -17,7 +17,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.nodes.*; @@ -38,7 +40,7 @@ import com.oracle.truffle.r.runtime.nodes.*; * <p> * TODO Consider using an {@link RLanguage} object to denote the call (somehow). */ -public class RErrorHandling implements RContext.StateFactory { +public class RErrorHandling { private static final int IN_HANDLER = 3; private static final RStringVector RESTART_CLASS = RDataFactory.createStringVectorFromScalar("restart"); @@ -67,7 +69,7 @@ public class RErrorHandling implements RContext.StateFactory { * Holds all the context-specific state that is relevant for error/warnings. Simple value class * for which geterrs/setters are unnecessary. */ - private static class ContextStateImpl implements RContext.ContextState { + public static class ContextStateImpl implements RContext.ContextState { /** * Values is either NULL or an RPairList, for {@code restarts}. */ @@ -127,6 +129,9 @@ public class RErrorHandling implements RContext.StateFactory { return dotSignalSimpleWarning; } + public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) { + return new ContextStateImpl(); + } } /** @@ -145,12 +150,8 @@ public class RErrorHandling implements RContext.StateFactory { private static final Object RESTART_TOKEN = new Object(); - public RContext.ContextState newContext(RContext context, Object... objects) { - return new ContextStateImpl(); - } - private static ContextStateImpl getRErrorHandlingState() { - return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.RErrorHandling); + return RContext.getInstance().stateRErrorHandling; } public static Object getHandlerStack() { @@ -435,7 +436,7 @@ public class RErrorHandling implements RContext.StateFactory { } // we are not quite done - need to check for options(error=expr) - Object errorExpr = RContext.getROptionsState().getValue("error"); + Object errorExpr = RContext.getInstance().stateROptions.getValue("error"); if (errorExpr != RNull.instance) { int oldInError = errorHandlingState.inError; try { @@ -525,7 +526,7 @@ public class RErrorHandling implements RContext.StateFactory { if (errorHandlingState.inWarning) { return; } - Object s = RContext.getROptionsState().getValue("warning.expression"); + Object s = RContext.getInstance().stateROptions.getValue("warning.expression"); if (s != RNull.instance) { if (!(s instanceof RLanguage || s instanceof RExpression)) { // TODO @@ -534,7 +535,12 @@ public class RErrorHandling implements RContext.StateFactory { } // ensured in ROptions - int w = ((RIntVector) RContext.getROptionsState().getValue("warn")).getDataAt(0); + + Object value = RContext.getInstance().stateROptions.getValue("warn"); + int w = 0; + if (value != RNull.instance) { + w = ((RAbstractIntVector) value).getDataAt(0); + } if (w == RRuntime.INT_NA) { w = 0; } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java index 69f0432a6ddea556c17f7e7fc4d74d44269ae071..58d173349e4044f524b9db3931e91ab7dbe7ae7a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java @@ -78,7 +78,7 @@ public final class RInternalError extends Error { } static String createVerboseStackTrace() { - if (FastROptions.PrintErrorStacktracesToFile.getValue() || FastROptions.PrintErrorStacktraces.getValue()) { + if (FastROptions.PrintErrorStacktracesToFile || FastROptions.PrintErrorStacktraces) { return Utils.createStackTrace(true); } else { return ""; @@ -86,7 +86,7 @@ public final class RInternalError extends Error { } public static void reportError(Throwable t) { - if (FastROptions.PrintErrorStacktracesToFile.getValue() || FastROptions.PrintErrorStacktraces.getValue()) { + if (FastROptions.PrintErrorStacktracesToFile || FastROptions.PrintErrorStacktraces) { ByteArrayOutputStream out = new ByteArrayOutputStream(); t.printStackTrace(new PrintStream(out)); String verboseStackTrace; @@ -97,11 +97,11 @@ public final class RInternalError extends Error { } else { verboseStackTrace = ""; } - if (FastROptions.PrintErrorStacktraces.getValue()) { + if (FastROptions.PrintErrorStacktraces) { System.err.println(out.toString()); System.err.println(verboseStackTrace); } - if (FastROptions.PrintErrorStacktracesToFile.getValue()) { + if (FastROptions.PrintErrorStacktracesToFile) { try (BufferedWriter writer = Files.newBufferedWriter(FileSystems.getDefault().getPath(REnvVars.rHome(), "fastr_errors.log"), StandardCharsets.UTF_8, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { writer.append(new Date().toString()).append('\n'); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java index ab98285672dfa9937afdd2c2c6f09f3d2226a5f8..9eb886fcc0d5297ca8fe64c71f3b3bf236db8199 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ROptions.java @@ -11,11 +11,12 @@ */ package com.oracle.truffle.r.runtime; -import static com.oracle.truffle.r.runtime.RCmdOptions.*; - import java.util.*; import java.util.Map.Entry; +import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.RContext.*; import com.oracle.truffle.r.runtime.data.*; /** @@ -24,23 +25,14 @@ import com.oracle.truffle.r.runtime.data.*; * * An unset option does not appear in the map but is represented as the value {@link RNull#instance} * . Setting with {@link RNull#instance} removes the option from the map and, therefore, from being - * visible in a call to {@code options()}. N.B. An option in the {@link #CHECKED_OPTIONS} set can - * never be removed and this is handled by checking the value passed on update, where + * visible in a call to {@code options()}. N.B. An option in the {@link #CHECKED_OPTIONS_SET} set + * can never be removed and this is handled by checking the value passed on update, where * {@link RNull#instance} is illegal. * */ -public class ROptions implements RContext.StateFactory { - public interface ContextState extends RContext.ContextState { - Set<Map.Entry<String, Object>> getValues(); - - Object getValue(String name); - - Object setValueNoCheck(String name, Object value); - - Object setValue(String name, Object value) throws OptionsException; - } +public class ROptions { - private static class ContextStateImpl implements ContextState { + public static final class ContextStateImpl implements RContext.ContextState { /** * The current values for a given context. */ @@ -87,6 +79,15 @@ public class ROptions implements RContext.StateFactory { return setValueNoCheck(name, coercedValue); } + public static ContextStateImpl newContext(RContext context, REnvVars envVars) { + HashMap<String, Object> map = new HashMap<>(); + if (context.getKind() == ContextKind.SHARE_NOTHING) { + applyDefaults(map, context.getOptions(), envVars); + } else { + map.putAll(context.getParent().stateROptions.map); + } + return new ContextStateImpl(map); + } } @SuppressWarnings("serial") @@ -100,64 +101,36 @@ public class ROptions implements RContext.StateFactory { } } - private static final String[] CHECKED_OPTIONS = new String[]{"width", "deparse.cutoff", "digits", "expressions", "keep.source", "editor", "continue", "prompt", "contrasts", "check.bounds", - "warn", "warning.length", "warning.expression", "max.print", "nwarnings", "error", "show.error.messages", "echo", "OutDec", "max.contour.segments", "rl_word_breaks", - "warnPartialMatchDollar", "warnPartialMatchArgs", "warnPartialMatchAttr", "showWarnCalls", "showErrorCalls", "showNCalls", "par.ask.default", "browserNLdisabled", "CBoundsCheck"}; - - private static final Set<String> CHECKED_OPTIONS_SET = new HashSet<>(); - - /** - * Holds the default values on startup of an {@link RContext}. - */ - private static final HashMap<String, Object> defaultsMap = new HashMap<>(); - private static HashMap<String, Object> systemInitMap; - - private static void initDefaults() { - for (String s : CHECKED_OPTIONS) { - CHECKED_OPTIONS_SET.add(s); - } - defaultsMap.put("add.smooth", RDataFactory.createLogicalVectorFromScalar(true)); - defaultsMap.put("check.bounds", RDataFactory.createLogicalVectorFromScalar(false)); - defaultsMap.put("continue", RDataFactory.createStringVector("+ ")); - defaultsMap.put("deparse.cutoff", RDataFactory.createIntVectorFromScalar(60)); - defaultsMap.put("digits", RDataFactory.createIntVectorFromScalar(7)); - defaultsMap.put("echo", RDataFactory.createLogicalVectorFromScalar(SLAVE.getValue() ? false : true)); - defaultsMap.put("encoding", RDataFactory.createStringVector("native.enc")); - defaultsMap.put("expressions", RDataFactory.createIntVectorFromScalar(5000)); - boolean keepPkgSource = optionFromEnvVar("R_KEEP_PKG_SOURCE"); - defaultsMap.put("keep.source", RDataFactory.createLogicalVectorFromScalar(keepPkgSource)); - defaultsMap.put("keep.source.pkgs", RDataFactory.createLogicalVectorFromScalar(keepPkgSource)); - defaultsMap.put("OutDec", RDataFactory.createStringVector(".")); - defaultsMap.put("prompt", RDataFactory.createStringVector("> ")); - defaultsMap.put("verbose", RDataFactory.createLogicalVectorFromScalar(false)); - defaultsMap.put("nwarnings", RDataFactory.createIntVectorFromScalar(50)); - defaultsMap.put("warning.length", RDataFactory.createIntVectorFromScalar(1000)); - defaultsMap.put("width", RDataFactory.createIntVectorFromScalar(80)); - defaultsMap.put("browserNLdisabled", RDataFactory.createLogicalVectorFromScalar(false)); - boolean cBoundsCheck = optionFromEnvVar("R_C_BOUNDS_CHECK"); - defaultsMap.put("CBoundsCheck", RDataFactory.createLogicalVectorFromScalar(cBoundsCheck)); - } - - public ContextState newContext(RContext context, Object... objects) { - if (defaultsMap.isEmpty()) { - initDefaults(); - } - HashMap<String, Object> map = new HashMap<>(); - map.putAll(systemInitMap == null ? defaultsMap : systemInitMap); - return new ContextStateImpl(map); - } - - @Override - public void systemInitialized(RContext context, RContext.ContextState state) { - ContextStateImpl optionsState = (ContextStateImpl) state; - systemInitMap = new HashMap<>(optionsState.map.size()); - systemInitMap.putAll(optionsState.map); + private static final Set<String> CHECKED_OPTIONS_SET = new HashSet<>(Arrays.asList("width", "deparse.cutoff", "digits", "expressions", "keep.source", "editor", "continue", "prompt", "contrasts", + "check.bounds", "warn", "warning.length", "warning.expression", "max.print", "nwarnings", "error", "show.error.messages", "echo", "OutDec", "max.contour.segments", + "rl_word_breaks", "warnPartialMatchDollar", "warnPartialMatchArgs", "warnPartialMatchAttr", "showWarnCalls", "showErrorCalls", "showNCalls", "par.ask.default", + "browserNLdisabled", "CBoundsCheck")); + + private static void applyDefaults(HashMap<String, Object> map, RCmdOptions options, REnvVars envVars) { + map.put("add.smooth", RDataFactory.createLogicalVectorFromScalar(true)); + map.put("check.bounds", RDataFactory.createLogicalVectorFromScalar(false)); + map.put("continue", RDataFactory.createStringVector("+ ")); + map.put("deparse.cutoff", RDataFactory.createIntVectorFromScalar(60)); + map.put("digits", RDataFactory.createIntVectorFromScalar(7)); + map.put("echo", RDataFactory.createLogicalVectorFromScalar(options.getBoolean(RCmdOption.SLAVE) ? false : true)); + map.put("encoding", RDataFactory.createStringVector("native.enc")); + map.put("expressions", RDataFactory.createIntVectorFromScalar(5000)); + boolean keepPkgSource = optionFromEnvVar("R_KEEP_PKG_SOURCE", envVars); + map.put("keep.source", RDataFactory.createLogicalVectorFromScalar(keepPkgSource)); + map.put("keep.source.pkgs", RDataFactory.createLogicalVectorFromScalar(keepPkgSource)); + map.put("OutDec", RDataFactory.createStringVector(".")); + map.put("prompt", RDataFactory.createStringVector("> ")); + map.put("verbose", RDataFactory.createLogicalVectorFromScalar(false)); + map.put("nwarnings", RDataFactory.createIntVectorFromScalar(50)); + map.put("warning.length", RDataFactory.createIntVectorFromScalar(1000)); + map.put("width", RDataFactory.createIntVectorFromScalar(80)); + map.put("browserNLdisabled", RDataFactory.createLogicalVectorFromScalar(false)); + boolean cBoundsCheck = optionFromEnvVar("R_C_BOUNDS_CHECK", envVars); + map.put("CBoundsCheck", RDataFactory.createLogicalVectorFromScalar(cBoundsCheck)); } - private static boolean optionFromEnvVar(String envVar) { - String envValue = REnvVars.get(envVar); - return envValue != null && envValue.equals("yes"); - + private static boolean optionFromEnvVar(String envVar, REnvVars envVars) { + return "yes".equals(envVars.get(envVar)); } private static Object check(String name, Object value) throws OptionsException { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java index 727795594e70bed87e4648077f512dd35a32d8cc..614a701256aa93a9a75ae6c593f65ac7ec14d220 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPerfStats.java @@ -188,7 +188,7 @@ public class RPerfStats { return; } reporting = true; - String file = FastROptions.PerfStatsFile.getValue(); + String file = FastROptions.PerfStatsFile; if (file != null) { try { out = new PrintStream(new FileOutputStream(file)); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java index f090fd858245f2712d657443ebd61d9f924f09c2..efed9307f1ae92aba748d13e20483308c995f75a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java @@ -22,24 +22,30 @@ */ package com.oracle.truffle.r.runtime; +import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.*; + import java.io.*; import java.nio.file.*; +import java.nio.file.FileSystem; import com.oracle.truffle.api.source.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.ffi.*; /** - * Handles the setup of system, site and user profile code. N.B. {@link #initialize()} only reads - * the files and leaves the evaluation to the caller, using {@link #siteProfile()} and - * {@link #userProfile()}. + * Handles the setup of system, site and user profile code. N.B. this class only reads the files and + * leaves the evaluation to the caller, using {@link #siteProfile()} and {@link #userProfile()}. */ -public class RProfile { - public static void initialize() { +public final class RProfile implements RContext.ContextState { + + private RProfile(RContext context, REnvVars envVars) { String rHome = REnvVars.rHome(); FileSystem fileSystem = FileSystems.getDefault(); + Source newSiteProfile = null; + Source newUserProfile = null; - if (!RCmdOptions.NO_SITE_FILE.getValue()) { - String siteProfilePath = REnvVars.get("R_PROFILE"); + if (!context.getOptions().getBoolean(NO_SITE_FILE)) { + String siteProfilePath = envVars.get("R_PROFILE"); if (siteProfilePath == null) { siteProfilePath = fileSystem.getPath(rHome, "etc", "Rprofile.site").toString(); } else { @@ -47,12 +53,12 @@ public class RProfile { } File siteProfileFile = new File(siteProfilePath); if (siteProfileFile.exists()) { - siteProfile = getProfile(siteProfilePath); + newSiteProfile = getProfile(siteProfilePath); } } - if (!RCmdOptions.NO_INIT_FILE.getValue()) { - String userProfilePath = REnvVars.get("R_PROFILE_USER"); + if (!context.getOptions().getBoolean(NO_INIT_FILE)) { + String userProfilePath = envVars.get("R_PROFILE_USER"); if (userProfilePath == null) { String dotRenviron = ".Rprofile"; userProfilePath = fileSystem.getPath(RFFIFactory.getRFFI().getBaseRFFI().getwd(), dotRenviron).toString(); @@ -65,15 +71,16 @@ public class RProfile { if (userProfilePath != null) { File userProfileFile = new File(userProfilePath); if (userProfileFile.exists()) { - userProfile = getProfile(userProfilePath); + newUserProfile = getProfile(userProfilePath); } } - } + siteProfile = newSiteProfile; + userProfile = newUserProfile; } - private static Source siteProfile; - private static Source userProfile; + private final Source siteProfile; + private final Source userProfile; public static Source systemProfile() { Path path = FileSystems.getDefault().getPath(REnvVars.rHome(), "library", "base", "R", "Rprofile"); @@ -84,11 +91,11 @@ public class RProfile { return source; } - public static Source siteProfile() { + public Source siteProfile() { return siteProfile; } - public static Source userProfile() { + public Source userProfile() { return userProfile; } @@ -101,4 +108,7 @@ public class RProfile { } } + public static RProfile newContext(RContext context, REnvVars envVars) { + return new RProfile(context, envVars); + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java index 0455314c2ee8f76228daaea9e98d44c4419bfc7c..5cdeca0edb151a16781b7bce6d67913e8edef1c7 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java @@ -18,6 +18,7 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.env.frame.*; @@ -291,8 +292,6 @@ public class RRuntime { } catch (NumberFormatException e) { if (exceptionOnFail) { throw e; - } else { - RContext.getInstance().getAssumptions().naIntroduced.invalidate(); } } return INT_NA; @@ -329,8 +328,6 @@ public class RRuntime { } if (exceptionOnFail) { throw e; - } else { - RContext.getInstance().getAssumptions().naIntroduced.invalidate(); } } return DOUBLE_NA; @@ -365,8 +362,6 @@ public class RRuntime { default: if (exceptionOnFail) { throw new NumberFormatException(); - } else { - RContext.getInstance().getAssumptions().naIntroduced.invalidate(); } return LOGICAL_NA; } @@ -805,7 +800,7 @@ public class RRuntime { return true; } if (type == RType.Function || type == RType.Closure || type == RType.Builtin || type == RType.Special) { - return obj instanceof RFunction; + return (obj instanceof RFunction) || (obj instanceof TruffleObject && !(obj instanceof RTypedValue)); } if (type == RType.Character) { return obj instanceof String || obj instanceof RStringVector; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java index 4388992444150d913c845629fdb9b0764bc20510..ed0c86ebc68153d35067f7ffe016f2fdcf775299 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java @@ -23,8 +23,10 @@ package com.oracle.truffle.r.runtime; import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.r.runtime.RContext.*; +import com.oracle.truffle.api.vm.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; /** * A collection of methods that need access to the AST types, needed by code that resides in the @@ -48,6 +50,8 @@ public interface RRuntimeASTAccess { */ RList asList(RLanguage rl); + Object fromList(RAbstractVector list); + /** * Get the "names" attribute for an {@link RLanguage} object, or {@code null} if none. */ @@ -148,9 +152,9 @@ public interface RRuntimeASTAccess { */ void setFunctionName(RootNode node, String name); - RContext create(RContext parent, Kind kind, String[] commandArgs, ConsoleHandler consoleHandler); + TruffleVM create(ContextInfo info); - RContext.Engine createEngine(RContext context); + Engine createEngine(RContext context); /** * Returns {@code true} iff {@code node} is an instance of {@code ReplacementNode}, which is not diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java index 5ce467cbbc3cbbd5489372d96598d25bb3b0e55f..c919e1990ec49259c6efac6489ff1c3fe87f6a0a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java @@ -17,9 +17,9 @@ import java.util.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.r.runtime.RContext.ContextState; -import com.oracle.truffle.r.runtime.RContext.Engine.ParseException; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute; import com.oracle.truffle.r.runtime.data.model.*; @@ -49,7 +49,7 @@ import com.oracle.truffle.r.runtime.instrument.*; * */ // Checkstyle: stop final class check -public class RSerialize implements RContext.StateFactory { +public class RSerialize { private static class Flags { static final int IS_OBJECT_BIT_MASK = 1 << 8; @@ -122,7 +122,7 @@ public class RSerialize implements RContext.StateFactory { Object eval(Object arg); } - private static class ContextStateImpl implements RContext.ContextState { + public static final class ContextStateImpl implements RContext.ContextState { /** * {@code true} iff we are saving the source from the deparse of an unserialized function * (for debugging later). @@ -147,10 +147,10 @@ public class RSerialize implements RContext.StateFactory { } return dotDotFindNamespace; } - } - public ContextState newContext(RContext context, Object... objects) { - return new ContextStateImpl(); + public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) { + return new ContextStateImpl(); + } } private static final int MAX_PACKED_INDEX = Integer.MAX_VALUE >> 8; @@ -232,7 +232,7 @@ public class RSerialize implements RContext.StateFactory { } private static ContextStateImpl getContextState() { - return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.RSerialize); + return RContext.getInstance().stateRSerialize; } /** diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java index df0ce5e9123731372fd3fea6b7f8da6e68d9cc4e..7b181fb60473c40c21c08061f100b7beca09b8ff 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java @@ -25,6 +25,7 @@ package com.oracle.truffle.r.runtime; import java.io.*; import java.net.*; import java.nio.file.*; +import java.nio.file.FileSystem; import java.nio.file.attribute.*; import java.util.*; @@ -34,8 +35,9 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.r.runtime.RContext.ConsoleHandler; +import com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption; import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; @@ -124,9 +126,10 @@ public final class Utils { */ public static RuntimeException exit(int status) { RPerfStats.report(); - if (RCmdOptions.DEBUGGER.getValue() != null) { + if (RContext.getInstance().getOptions().getString(RCmdOption.DEBUGGER) != null) { throw new DebugExitException(); } else { + RContext.getInstance().destroy(); System.exit(status); return null; } @@ -345,29 +348,33 @@ public final class Utils { str.append("\n"); } Frame unwrapped = RArguments.unwrap(frame); - RCaller call = RArguments.getCall(unwrapped); - if (call != null) { - RRuntimeASTAccess rASTAccess = RContext.getRRuntimeASTAccess(); - String callSrc = rASTAccess.getCallerSource(rASTAccess.getSyntaxCaller(call)); - str.append("Frame: ").append(callTarget).append(isVirtual ? " (virtual)" : ""); - str.append(" (called as: ").append(callSrc).append(')'); - } - if (printFrameSlots) { - FrameDescriptor frameDescriptor = unwrapped.getFrameDescriptor(); - for (FrameSlot s : frameDescriptor.getSlots()) { - str.append("\n ").append(s.getIdentifier()).append(" = "); - Object value = unwrapped.getValue(s); - try { - if (value instanceof RAbstractContainer && ((RAbstractContainer) value).getLength() > 32) { - str.append('<').append(value.getClass().getSimpleName()).append(" with ").append(((RAbstractContainer) value).getLength()).append(" elements>"); - } else { - String text = String.valueOf(value); - str.append(text.length() < 256 ? text : text.substring(0, 256) + "..."); + if (!RArguments.isRFrame(frame)) { + str.append("<unknown frame>"); + } else { + RCaller call = RArguments.getCall(unwrapped); + if (call != null) { + RRuntimeASTAccess rASTAccess = RContext.getRRuntimeASTAccess(); + String callSrc = rASTAccess.getCallerSource(rASTAccess.getSyntaxCaller(call)); + str.append("Frame: ").append(callTarget).append(isVirtual ? " (virtual)" : ""); + str.append(" (called as: ").append(callSrc).append(')'); + } + if (printFrameSlots) { + FrameDescriptor frameDescriptor = unwrapped.getFrameDescriptor(); + for (FrameSlot s : frameDescriptor.getSlots()) { + str.append("\n ").append(s.getIdentifier()).append(" = "); + Object value = unwrapped.getValue(s); + try { + if (value instanceof RAbstractContainer && ((RAbstractContainer) value).getLength() > 32) { + str.append('<').append(value.getClass().getSimpleName()).append(" with ").append(((RAbstractContainer) value).getLength()).append(" elements>"); + } else { + String text = String.valueOf(value); + str.append(text.length() < 256 ? text : text.substring(0, 256) + "..."); + } + } catch (Throwable t) { + // RLanguage values may not react kindly to getLength() calls + str.append("<exception ").append(t.getClass().getSimpleName()).append(" while printing value of type ").append(value == null ? "null" : value.getClass().getSimpleName()).append( + '>'); } - } catch (Throwable t) { - // RLanguage values may not react kindly to getLength() calls - str.append("<exception ").append(t.getClass().getSimpleName()).append(" while printing value of type ").append(value == null ? "null" : value.getClass().getSimpleName()).append( - '>'); } } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/VisibilityController.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/VisibilityController.java index 8cb7120ec3cad0db6b111602b58758e681a5bbb2..c75845186eadc1f72af2065545c810ecafe80d8e 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/VisibilityController.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/VisibilityController.java @@ -22,6 +22,8 @@ */ package com.oracle.truffle.r.runtime; +import com.oracle.truffle.r.runtime.context.*; + /** * This interface must be implemented by all nodes in the FastR implementation that control the * visibility of results in the shell. All specializations or, if no specializations exist, diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java index 0404e5901d79290900e295817ab083694c5a0183..0bcc330d21c6ffd5d3146da85caa05f7bb86763f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/ConnectionSupport.java @@ -29,26 +29,21 @@ import java.util.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.data.model.*; /** * Basic support classes and methods for the connection implementations. */ -public class ConnectionSupport implements RContext.StateFactory { - - public interface ContextState extends RContext.ContextState { - BaseRConnection getConnection(int index); - - RIntVector getAllConnections(); - } +public class ConnectionSupport { /** * The context specific state, which is the set of active connections. We stored these as weak * references to detect when they become unused whuch, when detected, results in them being * closed if still open. */ - private static final class ContextStateImpl implements ContextState { + public static final class ContextStateImpl implements RContext.ContextState { private static final int MAX_CONNECTIONS = 128; /** * Records all connections. The index in the array is the "descriptor" used in @@ -129,7 +124,8 @@ public class ConnectionSupport implements RContext.StateFactory { throw RError.error(RError.NO_NODE, RError.Message.ALL_CONNECTIONS_IN_USE); } - private void beforeDestroy() { + @Override + public void beforeDestroy(RContext context) { // close all open connections for (int i = 3; i <= hwm; i++) { WeakReference<BaseRConnection> ref = allConnections.get(i); @@ -170,20 +166,14 @@ public class ConnectionSupport implements RContext.StateFactory { } } } - } - private static ContextStateImpl getContextStateImpl() { - return (ContextStateImpl) RContext.getRConnectionState(); - } - - @Override - public ContextState newContext(RContext context, Object... objects) { - return new ContextStateImpl(); + public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) { + return new ContextStateImpl(); + } } - @Override - public void beforeDestroy(RContext context, RContext.ContextState state) { - ((ContextStateImpl) state).beforeDestroy(); + private static ContextStateImpl getContextStateImpl() { + return RContext.getInstance().stateRConnection; } private static final class ModeException extends IOException { @@ -334,7 +324,7 @@ public class ConnectionSupport implements RContext.StateFactory { */ public static RConnection fromVector(RVector vector, @SuppressWarnings("unused") RStringVector classAttr) { int index = RRuntime.asInteger(vector); - RConnection result = RContext.getRConnectionState().getConnection(index); + RConnection result = RContext.getInstance().stateRConnection.getConnection(index); if (result == null) { // non-existent connection, still legal according to GnuR // we cannot throw an error here as this can be used by parallel packages - do it lazily diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java index 9cc9bcb13af7c8a6519b9e84af571eda30e3ac1a..075d7006fddfbbf6d45fd4cbbcff4eeb800a98a3 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java @@ -26,14 +26,14 @@ import java.io.*; import java.nio.*; import java.util.*; -import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.ContextState; -import com.oracle.truffle.r.runtime.RContext.ConsoleHandler; -import com.oracle.truffle.r.runtime.conn.ConnectionSupport.*; +import com.oracle.truffle.r.runtime.conn.ConnectionSupport.AbstractOpenMode; +import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.model.*; -public class StdConnections implements RContext.StateFactory { +public class StdConnections { private static class Diversion { final RConnection conn; @@ -45,32 +45,31 @@ public class StdConnections implements RContext.StateFactory { } } - private static class ContextStateImpl implements RContext.ContextState { + public static final class ContextStateImpl implements RContext.ContextState { private final StdinConnection stdin; private final StdoutConnection stdout; private final StderrConnection stderr; private final Diversion[] diversions = new Diversion[20]; private int top = -1; - ContextStateImpl(StdinConnection stdin, StdoutConnection stdout, StderrConnection stderr) { + private ContextStateImpl(StdinConnection stdin, StdoutConnection stdout, StderrConnection stderr) { this.stdin = stdin; this.stdout = stdout; this.stderr = stderr; } - } - - public ContextState newContext(RContext context, Object... objects) { - ConsoleHandler consoleHandler = (ConsoleHandler) objects[0]; - try { - return new ContextStateImpl(new StdinConnection(), new StdoutConnection(consoleHandler), new StderrConnection(consoleHandler)); - } catch (IOException ex) { - throw Utils.fail("failed to open stdconnections:"); + public static ContextStateImpl newContext(RContext context) { + ConsoleHandler consoleHandler = context.getConsoleHandler(); + try { + return new ContextStateImpl(new StdinConnection(), new StdoutConnection(consoleHandler), new StderrConnection(consoleHandler)); + } catch (IOException ex) { + throw Utils.fail("failed to open stdconnections:"); + } } } private static ContextStateImpl getContextState() { - return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.StdConnections); + return RContext.getInstance().stateStdConnections; } public static RConnection getStdin() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ConsoleHandler.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ConsoleHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..ee957d9a6108158dec625e7a78ba7ea4c4257b9d --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ConsoleHandler.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.context; + +import com.oracle.truffle.api.CompilerDirectives.*; + +/** + * The interface to a source of input/output for the context, which may have different + * implementations for different contexts. Since I/O is involved, all methods are tagged with + * {@link TruffleBoundary} as a hint that so should the associated implementation methods. + */ +public interface ConsoleHandler { + /** + * Normal output with a new line. + */ + @TruffleBoundary + void println(String s); + + /** + * Normal output without a newline. + */ + @TruffleBoundary + void print(String s); + + /** + * Formatted output. + */ + @TruffleBoundary + default void printf(String format, Object... args) { + print(String.format(format, args)); + } + + /** + * Error output with a newline. + * + * @param s + */ + @TruffleBoundary + void printErrorln(String s); + + /** + * Error output without a newline. + */ + @TruffleBoundary + void printError(String s); + + /** + * Read a line of input, newline included in result. Returns null if {@link #isInteractive() == + * false}. The rationale for including the readline is to ensure that the accumulated input, + * whether it be from a file or the console accurately reflects the the source. TODO worry about + * "\r\n"? + */ + @TruffleBoundary + String readLine(); + + /** + * Denote whether the FastR instance is running in 'interactive' mode. This can be set in a + * number of ways and is <b>not</> simply equivalent to taking input from a file. However, it is + * final once set. + */ + @TruffleBoundary + boolean isInteractive(); + + /** + * Redirect error output to the normal output. + */ + @TruffleBoundary + void redirectError(); + + /** + * Get the current prompt. + */ + @TruffleBoundary + String getPrompt(); + + /** + * Set the R prompt. + */ + @TruffleBoundary + void setPrompt(String prompt); + + /** + * Get the console width. + */ + @TruffleBoundary + int getWidth(); + + String getInputDescription(); +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..4fcdc0b1d6a7c187a172c6694d8e4b676f80b70c --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.context; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +import com.oracle.truffle.api.vm.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.RContext.ContextKind; + +public final class ContextInfo { + private static final ConcurrentHashMap<Integer, ContextInfo> contextInfos = new ConcurrentHashMap<>(); + private static final AtomicInteger contextInfoIds = new AtomicInteger(); + + private final RCmdOptions options; + private final RContext.ContextKind kind; + private final TimeZone systemTimeZone; + + /** + * Any context created by another has a parent. When such a context is destroyed we must reset + * the RContext.threadLocalContext to the parent. + */ + private final RContext parent; + private final ConsoleHandler consoleHandler; + private final int id; + + private ContextInfo(RCmdOptions options, ContextKind kind, RContext parent, ConsoleHandler consoleHandler, TimeZone systemTimeZone, int id) { + this.options = options; + this.kind = kind; + this.parent = parent; + this.consoleHandler = consoleHandler; + this.systemTimeZone = systemTimeZone; + this.id = id; + } + + public TruffleVM newContext() { + contextInfos.remove(id); + return RContext.getRRuntimeASTAccess().create(this); + } + + /** + * Create a context configuration object. + * + * @param parent if non-null {@code null}, the parent creating the context + * @param kind defines the degree to which this context shares base and package environments + * with its parent + * @param options the command line arguments passed this R session + * @param consoleHandler a {@link ConsoleHandler} for output + * @param systemTimeZone the system's time zone + */ + public static ContextInfo create(RCmdOptions options, ContextKind kind, RContext parent, ConsoleHandler consoleHandler, TimeZone systemTimeZone) { + int id = contextInfoIds.incrementAndGet(); + return new ContextInfo(options, kind, parent, consoleHandler, systemTimeZone, id); + } + + public static ContextInfo create(RCmdOptions options, ContextKind kind, RContext parent, ConsoleHandler consoleHandler) { + return create(options, kind, parent, consoleHandler, TimeZone.getDefault()); + } + + public static int createDeferred(RCmdOptions options, ContextKind kind, RContext parent, ConsoleHandler consoleHandler) { + ContextInfo info = create(options, kind, parent, consoleHandler, TimeZone.getDefault()); + contextInfos.put(info.id, info); + return info.id; + } + + public static ContextInfo get(int id) { + return contextInfos.get(id); + } + + public RCmdOptions getOptions() { + return options; + } + + public ContextKind getKind() { + return kind; + } + + public RContext getParent() { + return parent; + } + + public ConsoleHandler getConsoleHandler() { + return consoleHandler; + } + + public TimeZone getSystemTimeZone() { + return systemTimeZone; + } + + public int getId() { + return id; + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/DefaultConsoleHandler.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/DefaultConsoleHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..9a3bcad867752a987576e42934921ba5a26dcc54 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/DefaultConsoleHandler.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.context; + +import java.io.*; + +import com.oracle.truffle.api.TruffleLanguage.Env; + +public class DefaultConsoleHandler implements ConsoleHandler { + + private final Env env; + private final BufferedReader in; + + public DefaultConsoleHandler(Env env) { + this.env = env; + in = new BufferedReader(env.stdIn()); + } + + public void println(String s) { + try { + env.stdOut().append(s).append('\n'); + } catch (IOException e) { + // TODO: handle this error + } + } + + public void print(String s) { + try { + env.stdOut().append(s).append('\n'); + } catch (IOException e) { + // TODO: handle this error + } + } + + public void printErrorln(String s) { + try { + env.stdOut().append(s).append('\n'); + } catch (IOException e) { + // TODO: handle this error + } + } + + public void printError(String s) { + try { + env.stdOut().append(s).append('\n'); + } catch (IOException e) { + // TODO: handle this error + } + } + + public String readLine() { + try { + return in.readLine(); + } catch (IOException e) { + // TODO: handle this error + return null; + } + } + + public boolean isInteractive() { + return true; + } + + public void redirectError() { + // ? + } + + public String getPrompt() { + return "> "; + } + + public void setPrompt(String prompt) { + // ? + } + + public int getWidth() { + return 80; + } + + public String getInputDescription() { + return "<TruffleVM env input>"; + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java new file mode 100644 index 0000000000000000000000000000000000000000..29b959bdebd85b255166ce77fe660aed6074b77b --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.context; + +import java.io.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.env.*; + +public interface Engine { + + public static class ParseException extends IOException { + private static final long serialVersionUID = 1L; + + private final Source source; + private final String token; + private final String substring; + private final int line; + + public ParseException(Throwable cause, Source source, String token, String substring, int line) { + super("parse exception", cause); + this.source = source; + this.token = token; + this.substring = substring; + this.line = line; + } + + public RError throwAsRError() { + if (source.getLineCount() == 1) { + throw RError.error(RError.NO_CALLER, RError.Message.UNEXPECTED, token, substring); + } else { + throw RError.error(RError.NO_CALLER, RError.Message.UNEXPECTED_LINE, token, substring, line); + } + } + + public void report(ConsoleHandler consoleHandler) { + String msg; + if (source.getLineCount() == 1) { + msg = String.format(RError.Message.UNEXPECTED.message, token, substring); + } else { + msg = String.format(RError.Message.UNEXPECTED_LINE.message, token, substring, line); + } + consoleHandler.println("Error: " + msg); + } + } + + public static final class IncompleteSourceException extends ParseException { + private static final long serialVersionUID = -6688699706193438722L; + + public IncompleteSourceException(Throwable cause, Source source, String token, String substring, int line) { + super(cause, source, token, substring, line); + } + } + + /** + * Make the engine ready for evaluations. + */ + void activate(REnvironment.ContextStateImpl stateREnvironment); + + /** + * Elapsed time of runtime. + * + * @return elapsed time in nanosecs. + */ + long elapsedTimeInNanos(); + + /** + * Return user and system times for any spawned child processes in nanosecs, < 0 means not + * available (Windows). + */ + long[] childTimesInNanos(); + + /** + * Parse an R expression and return an {@link RExpression} object representing the Truffle ASTs + * for the components. + */ + RExpression parse(Source source) throws ParseException; + + /** + * A (perhaps temporary) interface to support {@link TruffleLanguage}. + */ + CallTarget parseToCallTarget(Source source, boolean printResult) throws ParseException; + + /** + * Parse and evaluate {@code rscript} in {@code frame}. {@code printResult == true}, the result + * of the evaluation is printed to the console. + * + * @param sourceDesc a {@link Source} object that describes the input to be parsed + * @param frame the frame in which to evaluate the input + * @param printResult {@code true} iff the result of the evaluation should be printed to the + * console + * @return the object returned by the evaluation or {@code null} if an error occurred. + */ + Object parseAndEval(Source sourceDesc, MaterializedFrame frame, boolean printResult) throws ParseException; + + /** + * Variant of {@link #parseAndEval(Source, MaterializedFrame, boolean)} for evaluation in the + * global frame. + */ + Object parseAndEval(Source sourceDesc, boolean printResult) throws ParseException; + + /** + * Support for the {@code eval} {@code .Internal}. + */ + Object eval(RExpression expr, REnvironment envir, REnvironment enclos, int depth); + + /** + * Convenience method for common case. + */ + default Object eval(RExpression expr, REnvironment envir, int depth) { + return eval(expr, envir, null, depth); + } + + /** + * Variant of {@link #eval(RExpression, REnvironment, REnvironment, int)} for a single language + * element. + */ + Object eval(RLanguage expr, REnvironment envir, REnvironment enclos, int depth); + + /** + * Convenience method for common case. + */ + default Object eval(RLanguage expr, REnvironment envir, int depth) { + return eval(expr, envir, null, depth); + } + + /** + * Evaluate {@code expr} in {@code frame}. + */ + Object eval(RExpression expr, MaterializedFrame frame); + + /** + * Variant of {@link #eval(RExpression, MaterializedFrame)} for a single language element. + */ + Object eval(RLanguage expr, MaterializedFrame frame); + + /** + * Variant of {@link #eval(RLanguage, MaterializedFrame)} where we already have the + * {@link RFunction} and the evaluated arguments, but do not have a frame available, and we are + * behind a {@link TruffleBoundary}, so call inlining is not an issue. This is primarily used + * for R callbacks from {@link RErrorHandling} and {@link RSerialize}. + */ + Object evalFunction(RFunction func, Object... args); + + /** + * Evaluates an {@link com.oracle.truffle.r.runtime.data.RPromise.Closure} in {@code frame}. + */ + Object evalPromise(RPromise.Closure closure, MaterializedFrame frame); + + /** + * Checks for the existence of {@code .Last/.Last.sys} and if present and bound to a function, + * invokes the (parameterless) function. + */ + void checkAndRunLast(String name); + + /** + * Wraps the Truffle AST in {@code body} in an anonymous function and returns a + * {@link RootCallTarget} for it. + * + * N.B. For certain expressions, there might be some value in enclosing the wrapper function in + * a specific lexical scope. E.g., as a way to access names in the expression known to be + * defined in that scope. + * + * @param body The AST for the body of the wrapper, i.e., the expression being evaluated. + */ + RootCallTarget makePromiseCallTarget(Object body, String funName); + + /** + * Used by Truffle debugger; invokes the internal "print" support in R for {@code value}. + * Essentially this is equivalent to {@link #evalFunction} using the {@code "print"} function. + */ + void printResult(Object value); + + RFunction parseFunction(String name, Source source, MaterializedFrame enclosingFrame) throws ParseException; + + ForeignAccess getForeignAccess(RTypedValue value); + + Class<? extends TruffleLanguage<RContext>> getTruffleLanguage(); + +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java new file mode 100644 index 0000000000000000000000000000000000000000..0774e6e3fa49de90c17bed0bc567d66e353671d4 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.context; + +import java.util.*; +import java.util.concurrent.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.interop.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.vm.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RCmdOptions.Client; +import com.oracle.truffle.r.runtime.conn.*; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.env.*; +import com.oracle.truffle.r.runtime.ffi.*; +import com.oracle.truffle.r.runtime.rng.*; + +/** + * Encapsulates the runtime state ("context") of an R session. All access to that state from the + * implementation <b>must</b> go through this class. There can be multiple instances + * (multiple-tenancy) active within a single process/Java-VM. + * + * The context provides two sub-interfaces {@link ConsoleHandler} and {@link Engine}that are + * (typically) implemented elsewhere, and accessed through {@link #getConsoleHandler()} and + * {@link #getEngine()}, respectively. + * + * Context-specific state for implementation classes is managed by this class (or the associated + * engine) and accessed through the {@code getXXXState} methods. + * + * The life-cycle of a {@link RContext} is: + * <ol> + * <li>created: {@link #create(Env)}</li> + * <li>destroyed: {@link #destroy()}</li> + * </ol> + * + * Evaluations are only possible on an active context. + * + */ +public final class RContext extends ExecutionContext { + + public static final int CONSOLE_WIDTH = 80; + + public enum ContextKind { + /** + * Essentially a clean restart, modulo the basic VM-wide initialization. which does include, + * for example, reading the external environment variables. I.e., it is not a goal to create + * a multi-user environment with different external inputs. This kind of context can be used + * in a parallel computation. The initial context is always of this kind. The intent is that + * all mutable state is localized to the context, which may not be completely achievable. + * For example, shared native libraries are assumed to contain no mutable state and be + * re-entrant. + */ + SHARE_NOTHING, + + /** + * Shares the set of loaded packages of the parent context at the time the context is + * created. Only useful when there is a priori knowledge on the evaluation that the context + * will be used for. Cannot safely be used for parallel context evaluation. Must be created + * as a child of an existing parent context of type {@link #SHARE_NOTHING} or + * {@link #SHARE_PARENT_RO} and only one such child is allowed. (Strictly speaking the + * invariant should be only one active child, but the implementation enforces it at creation + * time). Evidently any changes made to the shared environment, e.g., loading a package, + * affect the parent. + */ + SHARE_PARENT_RW, + + /** + * Intermediate between {@link #SHARE_NOTHING} and {@link #SHARE_PARENT_RW}, this is similar + * to the standard shared code/copied data model provided by operating systems, although the + * code/data distinction isn't completely applicable to a language like R. Unlike + * {@link #SHARE_NOTHING}, where the ASTs for the functions in the default packages are + * distinct copies in each context, in this kind of context, they are shared. Strictly + * speaking, the bindings of R functions are shared, and this is achieved by creating a + * shallow copy of the environments associated with the default packages of the parent + * context at the time the context is created. + */ + SHARE_PARENT_RO; + + public static final ContextKind[] VALUES = values(); + } + + /** + * Tagging interface denoting a class that carries the context-specific state for a class that + * has context-specific state. The class specific state must implement this interface. + */ + public interface ContextState { + /** + * Called in response to the {@link RContext#destroy} method. Provides a hook for finalizing + * any state before the context is destroyed. + */ + @SuppressWarnings("unused") + default void beforeDestroy(RContext context) { + // default empty implementation + } + } + + /** + * A thread that is explicitly associated with a context for efficient lookup. + */ + public static class ContextThread extends Thread { + protected RContext context; + + public ContextThread(RContext context) { + this.context = context; + } + + protected ContextThread() { + + } + + public void setContext(RContext context) { + this.context = context; + } + } + + /** + * A thread for performing an evaluation (used by {@code fastr} package. + */ + public static class EvalThread extends ContextThread { + private final Source source; + private final ContextInfo info; + + public static final Map<Integer, Thread> threads = new ConcurrentHashMap<>(); + + public EvalThread(ContextInfo info, Source source) { + super(null); + this.info = info; + this.source = source; + threads.put(info.getId(), this); + } + + @Override + public void run() { + TruffleVM vm = info.newContext(); + setContext(truffleVMContexts.get(vm)); + try { + try { + context.engine.parseAndEval(source, true); + } catch (ParseException e) { + throw e.throwAsRError(); + } + } finally { + context.destroy(); + threads.remove(info.getId()); + } + } + } + + private final ContextInfo info; + private final Engine engine; + + /** + * Denote whether the result of an expression should be printed in the shell or not. + */ + private boolean resultVisible = true; + + /** + * A context-specific value that is checked in {@code HiddenInternalFunctions} to avoid an error + * report on a {@code SUBSTITUTE} builtin. Not worth promoting to a {@link ContextState}. + */ + private boolean loadingBase; + + /** + * At most one shared child. + */ + private RContext sharedChild; + + /** + * Back pointer to the evalThread. + */ + private EvalThread evalThread; + + /** + * Typically there is a 1-1 relationship between an {@link RContext} and the thread that is + * performing the evaluation, so we can store the {@link RContext} in a {@link ThreadLocal}. + * + * When a context is first created no threads are attached, to allow contexts to be used as + * values in the experimental {@code fastr.createcontext} function. Additional threads can be + * added by the {@link #attachThread} method. + */ + public static final ThreadLocal<RContext> threadLocalContext = new ThreadLocal<>(); + + /** + * Used by the MethodListDispatch class. + */ + + private boolean methodTableDispatchOn = true; + + private boolean active; + + /** + * A (hopefully) temporary workaround to ignore the setting of {@link #resultVisible} for + * benchmarks. Set across all contexts. + */ + @CompilationFinal private static boolean ignoreVisibility; + + /* + * Workarounds to finesse project circularities between runtime/nodes. + */ + @CompilationFinal private static RRuntimeASTAccess runtimeASTAccess; + @CompilationFinal private static RBuiltinLookup builtinLookup; + + /** + * Initialize VM-wide static values. + */ + public static void initialize(RRuntimeASTAccess rASTHelperArg, RBuiltinLookup rBuiltinLookupArg, boolean ignoreVisibilityArg) { + runtimeASTAccess = rASTHelperArg; + builtinLookup = rBuiltinLookupArg; + ignoreVisibility = ignoreVisibilityArg; + } + + /** + * Associates this {@link RContext} with the current thread. + */ + public void attachThread() { + Thread current = Thread.currentThread(); + if (current instanceof ContextThread) { + ((ContextThread) current).setContext(this); + } else { + threadLocalContext.set(this); + } + } + + /** + * Waits for the associated EvalThread to finish. + * + * @throws InterruptedException + */ + public void joinThread() throws InterruptedException { + EvalThread t = this.evalThread; + if (t == null) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "no eval thread in a given context"); + } + this.evalThread = null; + t.join(); + } + + private static final Assumption singleContextAssumption = Truffle.getRuntime().createAssumption("single RContext"); + @CompilationFinal private static RContext singleContext; + + private final Env env; + private final HashMap<String, TruffleObject> exportedSymbols = new HashMap<>(); + + /** + * The set of classes for which the context manages context-specific state, and their state. We + * could do this more dynamically with a registration process, perhaps driven by an annotation + * processor, but the set is relatively small, so we just enumerate them here. + */ + public final REnvVars stateREnvVars; + public final RProfile stateRProfile; + public final ROptions.ContextStateImpl stateROptions; + public final REnvironment.ContextStateImpl stateREnvironment; + public final RErrorHandling.ContextStateImpl stateRErrorHandling; + public final ConnectionSupport.ContextStateImpl stateRConnection; + public final StdConnections.ContextStateImpl stateStdConnections; + public final RRNG.ContextStateImpl stateRNG; + public final ContextState stateRFFI; + public final RSerialize.ContextStateImpl stateRSerialize; + + private ContextState[] contextStates() { + return new ContextState[]{stateREnvVars, stateRProfile, stateROptions, stateREnvironment, stateRErrorHandling, stateRConnection, stateStdConnections, stateRNG, stateRFFI, stateRSerialize}; + } + + private RContext(Env env) { + if (tempInitializingContextInfo == null) { + this.info = ContextInfo.create(RCmdOptions.parseArguments(Client.R, new String[0]), ContextKind.SHARE_NOTHING, null, new DefaultConsoleHandler(env)); + } else { + this.info = tempInitializingContextInfo; + lastContext = this; + } + + this.env = env; + if (info.getConsoleHandler() == null) { + throw Utils.fail("no console handler set"); + } + + if (singleContextAssumption.isValid()) { + if (singleContext == null) { + singleContext = this; + } else { + singleContext = null; + singleContextAssumption.invalidate(); + } + } + engine = RContext.getRRuntimeASTAccess().createEngine(this); + + /* + * Activate the context by attaching the current thread and initializing the {@link + * ContextState} objects. Note that we attach the thread before creating the new context + * state. This means that code that accesses the state through this interface will receive a + * {@code null} value. Access to the parent state is available through the {@link RContext} + * argument passed to the newContext methods. It might be better to attach the thread after + * state creation but it is a finely balanced decision and risks incorrectly accessing the + * parent state. + */ + assert !active; + active = true; + attachThread(); + stateREnvVars = REnvVars.newContext(this); + stateRProfile = RProfile.newContext(this, stateREnvVars); + stateROptions = ROptions.ContextStateImpl.newContext(this, stateREnvVars); + stateREnvironment = REnvironment.ContextStateImpl.newContext(this); + stateRErrorHandling = RErrorHandling.ContextStateImpl.newContext(this); + stateRConnection = ConnectionSupport.ContextStateImpl.newContext(this); + stateStdConnections = StdConnections.ContextStateImpl.newContext(this); + stateRNG = RRNG.ContextStateImpl.newContext(this); + stateRFFI = RFFIContextStateFactory.newContext(this); + stateRSerialize = RSerialize.ContextStateImpl.newContext(this); + engine.activate(stateREnvironment); + + if (info.getKind() == ContextKind.SHARE_PARENT_RW) { + if (info.getParent().sharedChild != null) { + throw RError.error(RError.NO_NODE, RError.Message.GENERIC, "can't have multiple active SHARED_PARENT_RW contexts"); + } + info.getParent().sharedChild = this; + } + for (ContextState state : contextStates()) { + assert state != null; + } + } + + /** + * Create a context with the given configuration. + */ + public static RContext create(Env env) { + return new RContext(env); + } + + /** + * Destroy this context. + */ + public void destroy() { + for (ContextState state : contextStates()) { + state.beforeDestroy(this); + } + if (info.getKind() == ContextKind.SHARE_PARENT_RW) { + info.getParent().sharedChild = null; + } + if (info.getParent() == null) { + threadLocalContext.set(null); + } else { + threadLocalContext.set(info.getParent()); + } + } + + public RContext getParent() { + return info.getParent(); + } + + public Env getEnv() { + return env; + } + + public ContextKind getKind() { + return info.getKind(); + } + + @TruffleBoundary + private static RContext getInstanceInternal() { + RContext result = threadLocalContext.get(); + assert result != null; + assert result.active; + return result; + } + + public static RContext getInstance() { + RContext context = singleContext; + if (context != null) { + try { + singleContextAssumption.check(); + return context; + } catch (InvalidAssumptionException e) { + // fallback to slow case + } + } + + Thread current = Thread.currentThread(); + if (current instanceof ContextThread) { + context = ((ContextThread) current).context; + assert context != null; + return context; + } else { + return getInstanceInternal(); + } + } + + /** + * Access to the engine, when an {@link RContext} object is available, and/or when {@code this} + * context is not active. + */ + public Engine getThisEngine() { + return engine; + } + + public boolean isVisible() { + return resultVisible; + } + + public void setVisible(boolean v) { + resultVisible = v; + } + + public boolean isMethodTableDispatchOn() { + return methodTableDispatchOn; + } + + public void setMethodTableDispatchOn(boolean on) { + methodTableDispatchOn = on; + } + + public boolean isInteractive() { + return info.getConsoleHandler().isInteractive(); + } + + public static boolean isIgnoringVisibility() { + return ignoreVisibility; + } + + public ConsoleHandler getConsoleHandler() { + return info.getConsoleHandler(); + } + + /** + * This is a static property of the implementation and not context-specific. + */ + public static RRuntimeASTAccess getRRuntimeASTAccess() { + return runtimeASTAccess; + } + + /** + * Is {@code name} a builtin function (but not a {@link RBuiltinKind#INTERNAL}? + */ + public static boolean isPrimitiveBuiltin(String name) { + return builtinLookup.isPrimitiveBuiltin(name); + } + + /** + * Return the {@link RFunction} for the builtin {@code name}. + */ + @TruffleBoundary + public static RFunction lookupBuiltin(String name) { + return builtinLookup.lookupBuiltin(name); + } + + /** + * Returns the descriptor for the builtin with the given name. This does not cause an RFunction + * to be created. + */ + public static RBuiltinDescriptor lookupBuiltinDescriptor(String name) { + return builtinLookup.lookupBuiltinDescriptor(name); + } + + public RCmdOptions getOptions() { + return info.getOptions(); + } + + @Override + public String toString() { + return "context: " + info.getId(); + } + + /* + * static functions necessary in code where the context is only implicit in the thread(s) + * running an evaluation + */ + + public static Engine getEngine() { + return RContext.getInstance().engine; + } + + public void setLoadingBase(boolean b) { + loadingBase = b; + } + + public boolean getLoadingBase() { + return loadingBase; + } + + public Map<String, TruffleObject> getExportedSymbols() { + return exportedSymbols; + } + + public TimeZone getSystemTimeZone() { + return info.getSystemTimeZone(); + } + + /* + * TODO: this fields are used to convey initialization information from outside TruffleVM to + * RContext. need to be replaced with a mechanism provided by TruffleVM once this is available. + */ + public static ContextInfo tempInitializingContextInfo; + private static RContext lastContext; + private static final Map<TruffleVM, RContext> truffleVMContexts = new HashMap<>(); + + // TODO: destroying a TruffleVM should be handled in TruffleVM itself + public static void destroyContext(TruffleVM vm) { + truffleVMContexts.get(vm).destroy(); + } + + public static void associate(TruffleVM vm) { + assert lastContext != null; + truffleVMContexts.put(vm, lastContext); + lastContext = null; + tempInitializingContextInfo = null; + } + + public static RContext fromTruffleVM(TruffleVM vm) { + return truffleVMContexts.get(vm); + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java index 24317054af712640643d2a0d93500373aa282e9a..af6b20276f821086b52e3c438bf205534ddf307d 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplexVector.java @@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.ops.na.*; -public final class RComplexVector extends RVector implements RAbstractComplexVector, RAccessibleStore<double[]> { +public final class RComplexVector extends RVector implements RAbstractComplexVector { public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Complex.getName()); @@ -74,6 +74,24 @@ public final class RComplexVector extends RVector implements RAbstractComplexVec } } + public void setDataAt(Object store, int index, RComplex value) { + assert data == store; + double[] array = (double[]) store; + array[index << 1] = value.getRealPart(); + array[(index << 1) + 1] = value.getImaginaryPart(); + } + + public RComplex getDataAt(Object store, int i) { + assert data == store; + double[] doubleStore = (double[]) store; + int index = i << 1; + return RDataFactory.createComplex(doubleStore[index], doubleStore[index + 1]); + } + + public RComplex getDataAt(int i) { + return getDataAt(data, i); + } + @Override public String toString() { return toString(i -> RRuntime.complexToString(getDataAt(i))); @@ -91,17 +109,6 @@ public final class RComplexVector extends RVector implements RAbstractComplexVec return true; } - public RAbstractVector setDataAt(int index, RComplex value) { - data[index << 1] = value.getRealPart(); - data[(index << 1) + 1] = value.getImaginaryPart(); - return this; - } - - public RComplex getDataAt(int i) { - int index = i << 1; - return RDataFactory.createComplex(data[index], data[index + 1]); - } - public double[] getDataCopy() { double[] copy = new double[data.length]; System.arraycopy(data, 0, copy, 0, data.length); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java index 51f5b921768ebb4375349479ae889304166b1bb8..dab55fab2e1249ca4e023351df48c6191b1316c9 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFrame.java @@ -154,8 +154,7 @@ public final class RDataFrame implements RShareable, RAbstractContainer { @Override public RList getDimNames(RAttributeProfiles attrProfiles) { - RInternalError.unimplemented("data frame's dimnames needs to be obtained using builtins"); - return null; + return vector.getDimNames(attrProfiles); } @Override @@ -226,4 +225,9 @@ public final class RDataFrame implements RShareable, RAbstractContainer { return vector.setClassAttr(classAttr, convertToInt); } + @Override + public String toString() { + return "RDataFrame [vector=" + vector + ", length=" + length + "]"; + } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java index 53dc236f9121f5f1f8385cc66370dd7769d1a5a6..0a9ed7a4f52ebcf044b46b3c59f2e2fe58987ef3 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDoubleVector.java @@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.ops.na.*; -public final class RDoubleVector extends RVector implements RAbstractDoubleVector, RAccessibleStore<double[]> { +public final class RDoubleVector extends RVector implements RAbstractDoubleVector { public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Numeric.getName()); @@ -73,6 +73,16 @@ public final class RDoubleVector extends RVector implements RAbstractDoubleVecto return data; } + public void setDataAt(Object store, int index, double value) { + assert data == store; + ((double[]) store)[index] = value; + } + + public double getDataAt(Object store, int index) { + assert data == store; + return ((double[]) store)[index]; + } + public RDoubleVector copyResetData(double[] newData) { boolean isComplete = true; for (int i = 0; i < newData.length; i++) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java index 917cac281c727428c65cd3c9222061c6e108f005..83738de2a4f1facbb40b84997c7ee3b6e24a1420 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java @@ -204,4 +204,9 @@ public class RExpression implements RShareable, RAbstractContainer { return data.setClassAttr(classAttr, convertToInt); } + @Override + public String toString() { + return String.format("RExpression(data=%s)", data); + } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java index 419f3178795b73f77f8ff44bcc770df04859ed88..36f05605a20550ccd9655de6a6359943d3a79e28 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFactor.java @@ -36,6 +36,10 @@ public final class RFactor implements RShareable, RAbstractContainer { this.ordered = ordered; } + public int[] getInternalStore() { + return vector.getInternalStore(); + } + public RType getRType() { return RType.Integer; } @@ -221,4 +225,5 @@ public final class RFactor implements RShareable, RAbstractContainer { RVector levels = getLevels(attrProfiles); return levels == null ? 0 : levels.getLength(); } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java index 2e71e9e77a0de8e4c193d35c8b11da3d8a9dbcb6..79271368c0edb9ce33fa50b75e069ec645286312 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java @@ -24,8 +24,10 @@ package com.oracle.truffle.r.runtime.data; import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.interop.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; /** * An instance of {@link RFunction} represents a function defined in R. The properties of a function @@ -40,7 +42,7 @@ import com.oracle.truffle.r.runtime.*; * {@link #enclosingFrame}. * </ul> */ -public final class RFunction extends RAttributeStorage implements RTypedValue { +public final class RFunction extends RAttributeStorage implements RTypedValue, TruffleObject { private String name; private final RootCallTarget target; @@ -111,4 +113,8 @@ public final class RFunction extends RAttributeStorage implements RTypedValue { this.name = name; RContext.getRRuntimeASTAccess().setFunctionName(getRootNode(), name); } + + public ForeignAccess getForeignAccess() { + return RContext.getEngine().getForeignAccess(this); + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java index 8b4c4c9eb8cf1ae5e20a12c62504b5deb403e050..f5a1de57906b3da71b428059ea6f72e948cdee37 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RIntVector.java @@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.ops.na.*; -public final class RIntVector extends RVector implements RAbstractIntVector, RAccessibleStore<int[]> { +public final class RIntVector extends RVector implements RAbstractIntVector { public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Integer.getName()); @@ -72,6 +72,17 @@ public final class RIntVector extends RVector implements RAbstractIntVector, RAc return data[index]; } + public int getDataAt(Object store, int index) { + assert data == store; + return ((int[]) store)[index]; + } + + @Override + public void setDataAt(Object store, int index, int value) { + assert data == store; + ((int[]) store)[index] = value; + } + @Override protected RIntVector internalCopy() { return new RIntVector(Arrays.copyOf(data, data.length), isComplete(), null); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java index 897811278fea534b6942adb7d4d0550c2def9b72..d1add494cc24afb06a6b3a89de9c865faf6524be 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java @@ -24,6 +24,7 @@ package com.oracle.truffle.r.runtime.data; import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.nodes.*; @@ -241,4 +242,9 @@ public class RLanguage extends RLanguageRep implements RAbstractContainer, RAttr refCount--; } + @Override + public String toString() { + return String.format("RLanguage(rep=%s)", getRep()); + } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java index d8af43553f6e618ce05dedc3098ab9efa9a0e70b..b1b8231dcae98c5f931b6f6bed6ae58c22193981 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java @@ -25,9 +25,8 @@ package com.oracle.truffle.r.runtime.data; import java.util.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.data.model.*; -public final class RList extends RListBase implements RAbstractVector, RGPBits { +public final class RList extends RListBase implements RGPBits { private static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.List.getName()); @@ -38,8 +37,9 @@ public final class RList extends RListBase implements RAbstractVector, RGPBits { super(data, dims, names); } - public RType getRType() { - return RType.List; + @Override + public RList materialize() { + return this; } @Override diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java index 9d9c948089e2845e8a4d656c078aeee7d153be1f..852c5ae1508227bfa7838f0f762ccc4054362ea6 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java @@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.ops.na.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -public abstract class RListBase extends RVector implements RAbstractVector { +public abstract class RListBase extends RVector implements RAbstractListVector { protected final Object[] data; @@ -44,6 +44,22 @@ public abstract class RListBase extends RVector implements RAbstractVector { return data.length; } + public Object[] getInternalStore() { + return data; + } + + @Override + public Object getDataAtAsObject(Object store, int index) { + assert store == data; + return ((Object[]) store)[index]; + } + + @Override + public void setDataAt(Object store, int index, Object value) { + assert store == data; + ((Object[]) store)[index] = value; + } + @Override public String toString() { return toString(i -> RRuntime.toString(getDataAt(i))); @@ -138,11 +154,6 @@ public abstract class RListBase extends RVector implements RAbstractVector { } } - @Override - public final RVector materialize() { - return this; - } - @Override public final Object getDataAtAsObject(int index) { return this.getDataAt(index); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java index 47c423143384a6aa3150559b8b977c0f3c8648a7..596334df9a89ed860132837fc9474211db58c595 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogicalVector.java @@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.ops.na.*; -public final class RLogicalVector extends RVector implements RAbstractLogicalVector, RAccessibleStore<byte[]> { +public final class RLogicalVector extends RVector implements RAbstractLogicalVector { public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Logical.getName()); @@ -70,6 +70,18 @@ public final class RLogicalVector extends RVector implements RAbstractLogicalVec return data; } + @Override + public void setDataAt(Object store, int index, byte value) { + assert data == store; + ((byte[]) store)[index] = value; + } + + @Override + public byte getDataAt(Object store, int index) { + assert data == store; + return ((byte[]) store)[index]; + } + @Override protected RLogicalVector internalCopy() { return new RLogicalVector(Arrays.copyOf(data, data.length), isComplete(), null); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java index 61f9c8c92689fb8fd12bb0fbd77eaa1872d41939..0944e9a4f6665608b7415634e70d0dba55406f9c 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java @@ -28,6 +28,7 @@ import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.frame.FrameInstance.FrameAccess; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.nodes.*; /** diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java index 63336ae9fa41ba4bfb861ab4a7ccd6b1bfa5d43f..b65ea90dabfc91a4aea2f020bec828abf9faade5 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java @@ -77,6 +77,19 @@ public final class RRaw extends RScalarVector implements RAbstractRawVector { return value; } + @Override + public boolean equals(Object obj) { + if (obj instanceof RRaw) { + return value == ((RRaw) obj).value; + } + return super.equals(obj); + } + + @Override + public int hashCode() { + return value; + } + @Override public String toString() { CompilerAsserts.neverPartOfCompilation(); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java index 13c1c48c2cd71e19b56db4ca6f1a8b8586ead7c4..e478f89a76fdc1ee3a8c3ff5aafa208513dea5e5 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRawVector.java @@ -29,7 +29,7 @@ import com.oracle.truffle.r.runtime.data.closures.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.ops.na.*; -public final class RRawVector extends RVector implements RAbstractRawVector, RAccessibleStore<byte[]> { +public final class RRawVector extends RVector implements RAbstractRawVector { public static final RStringVector implicitClassHeader = RDataFactory.createStringVectorFromScalar(RType.Raw.getName()); @@ -66,10 +66,22 @@ public final class RRawVector extends RVector implements RAbstractRawVector, RAc return data[index]; } + @Override + public byte getRawDataAt(Object store, int index) { + assert data == store; + return ((byte[]) store)[index]; + } + public byte[] getInternalStore() { return data; } + @Override + public void setRawDataAt(Object store, int index, byte value) { + assert data == store; + ((byte[]) store)[index] = value; + } + @Override protected RRawVector internalCopy() { return new RRawVector(Arrays.copyOf(data, data.length), null); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java index 38307480a0ac7ffe3f0ab19f56e589650c71cebd..fd07f8187eaf52a935909185d16b5567c59d663b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java @@ -66,11 +66,11 @@ public final class RString extends RScalarVector implements RAbstractStringVecto } public RStringVector materialize() { - return RDataFactory.createStringVector(getValue()); + return RDataFactory.createStringVector(new String[]{getValue()}, isComplete()); } @Override public boolean isNA() { - return false; + return !RRuntime.isComplete(value); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java index 012e629d4b3648f7d84672ae763fdb811edc3474..171a295943d5785896936ad45c4bbe8149bc5da4 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringVector.java @@ -25,6 +25,7 @@ package com.oracle.truffle.r.runtime.data; import java.util.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.closures.*; import com.oracle.truffle.r.runtime.data.model.*; import com.oracle.truffle.r.runtime.ops.na.*; @@ -44,6 +45,33 @@ public final class RStringVector extends RVector implements RAbstractStringVecto this(data, complete, dims, null); } + public RAbstractVector castSafe(RType type) { + switch (type) { + case Character: + return this; + case List: + return RClosures.createAbstractVectorToListVector(this); + default: + return null; + } + } + + public String[] getInternalStore() { + return data; + } + + @Override + public void setDataAt(Object store, int index, String value) { + assert data == store; + ((String[]) store)[index] = value; + } + + @Override + public String getDataAt(Object store, int index) { + assert data == store; + return ((String[]) store)[index]; + } + @Override protected RStringVector internalCopy() { return new RStringVector(Arrays.copyOf(data, data.length), isComplete(), null); @@ -96,7 +124,13 @@ public final class RStringVector extends RVector implements RAbstractStringVecto @Override protected boolean internalVerify() { - // TODO: Implement String + NA + if (isComplete()) { + for (String b : data) { + if (b == RRuntime.STRING_NA) { + return false; + } + } + } return true; } @@ -153,7 +187,7 @@ public final class RStringVector extends RVector implements RAbstractStringVecto return RDataFactory.createStringVector(copyResizedData(size, fillNA ? RRuntime.STRING_NA : null), isComplete); } - RStringVector resizeWithEmpty(int size) { + public RStringVector resizeWithEmpty(int size) { return RDataFactory.createStringVector(createResizedData(size, RRuntime.NAMES_ATTR_EMPTY_VALUE), isComplete()); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToStringVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToStringVectorClosure.java index 0de20b825ecee116df0de10f875a1fc524ad8926..8d78bf828e00571268c3506cca5ac541328a5b4a 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToStringVectorClosure.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToStringVectorClosure.java @@ -42,7 +42,7 @@ public abstract class RToStringVectorClosure extends RToVectorClosure implements String data = getDataAt(i); result[i] = data; } - return RDataFactory.createStringVector(result, vector.isComplete()); + return RDataFactory.createStringVector(result, vector.isComplete(), getDimensions(), getNames(null)); } @Override diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java index ff2a29d8b762fcf963126a96944a4ffa4c74ab7b..0a3c9ade1e94df5ec8b39729985344688146a79f 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java @@ -38,6 +38,10 @@ public abstract class RToVectorClosure implements RAbstractVector { return vector.getLength(); } + public Void getInternalStore() { + return null; + } + @Override public final RAbstractContainer resize(int size) { return vector.resize(size); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractComplexVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractComplexVector.java index f064fbe5413a18a353c70dc2c583d94203b520eb..1abd29300cf169ffd4acbdeb4c69d62178689b49 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractComplexVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractComplexVector.java @@ -34,8 +34,21 @@ public interface RAbstractComplexVector extends RAbstractVector { RComplex getDataAt(int index); + default RComplex getDataAt(@SuppressWarnings("unused") Object store, int index) { + return getDataAt(index); + } + RComplexVector materialize(); + @SuppressWarnings("unused") + default void setDataAt(Object store, int index, RComplex value) { + throw new UnsupportedOperationException(); + } + + default void setNA(Object store, int index) { + setDataAt(store, index, RComplex.NA); + } + default boolean checkCompleteness() { for (int i = 0; i < getLength(); i++) { if (RRuntime.isNA(getDataAt(i))) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractContainer.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractContainer.java index 148785b2db098d7d7be8a6b862de75af19b425ec..f5a12ad39d430efa5a226f25e8860fabd8a7f42b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractContainer.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractContainer.java @@ -46,6 +46,14 @@ public interface RAbstractContainer extends RAttributable, RTypedValue { Object getDataAtAsObject(int index); + default Object getDataAtAsObject(@SuppressWarnings("unused") Object store, int index) { + return getDataAtAsObject(index); + } + + default Object getInternalStore() { + return null; + } + RStringVector getNames(RAttributeProfiles attrProfiles); void setNames(RStringVector newNames); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractDoubleVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractDoubleVector.java index 0a1ef6604b5c4634995e1ccb1b91b9f42794986f..83b9821a6068186fcaf9c082edc4198affd0493d 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractDoubleVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractDoubleVector.java @@ -32,8 +32,21 @@ public interface RAbstractDoubleVector extends RAbstractVector { return getDataAt(index); } + default double getDataAt(@SuppressWarnings("unused") Object store, int index) { + return getDataAt(index); + } + double getDataAt(int index); + @SuppressWarnings("unused") + default void setDataAt(Object store, int index, double value) { + throw new UnsupportedOperationException(); + } + + default void setNA(Object store, int index) { + setDataAt(store, index, RRuntime.DOUBLE_NA); + } + RDoubleVector materialize(); default boolean checkCompleteness() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractIntVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractIntVector.java index ce8195278734c36440cc59ca2b44d2eed1e20766..8bc658e4072af3e538425a6f0dfb70f2c472f20b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractIntVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractIntVector.java @@ -32,8 +32,21 @@ public interface RAbstractIntVector extends RAbstractVector { return getDataAt(index); } + default int getDataAt(@SuppressWarnings("unused") Object store, int index) { + return getDataAt(index); + } + int getDataAt(int index); + @SuppressWarnings("unused") + default void setDataAt(Object store, int index, int value) { + throw new UnsupportedOperationException(); + } + + default void setNA(Object store, int index) { + setDataAt(store, index, RRuntime.INT_NA); + } + RIntVector materialize(); default boolean checkCompleteness() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractListVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractListVector.java index 1b657ab5838f3baad84d47d4700fe351e3c01f0c..e577a15db7ca17fa9b7e45d95c995f12af5272fe 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractListVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractListVector.java @@ -30,6 +30,10 @@ public interface RAbstractListVector extends RAbstractVector { @Override Object getDataAtAsObject(int index); + default Object getDataAtAsObject(Object store, int i) { + return getDataAtAsObject(i); + } + RList materialize(); default boolean checkCompleteness() { @@ -44,4 +48,13 @@ public interface RAbstractListVector extends RAbstractVector { return Object.class; } + @SuppressWarnings("unused") + default void setDataAt(Object store, int index, Object value) { + throw new UnsupportedOperationException(); + } + + default void setNA(Object store, int index) { + setDataAt(store, index, RNull.instance); + } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractLogicalVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractLogicalVector.java index 6510c71fec605664f5ef200a274417b8be0a3eaa..0327514ee99898f5ff4fefa5f4ac79c2e6aeb6bf 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractLogicalVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractLogicalVector.java @@ -32,8 +32,21 @@ public interface RAbstractLogicalVector extends RAbstractVector { return getDataAt(index); } + default byte getDataAt(@SuppressWarnings("unused") Object store, int index) { + return getDataAt(index); + } + byte getDataAt(int index); + @SuppressWarnings("unused") + default void setDataAt(Object store, int index, byte value) { + throw new UnsupportedOperationException(); + } + + default void setNA(Object store, int index) { + setDataAt(store, index, RRuntime.LOGICAL_NA); + } + RLogicalVector materialize(); default boolean checkCompleteness() { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractRawVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractRawVector.java index 398ed92d27adb163a55bda7e52d10576ea782c36..126ec1dfadec0d78b85e9f9809851e6d4b040e75 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractRawVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractRawVector.java @@ -32,6 +32,18 @@ public interface RAbstractRawVector extends RAbstractVector { return getDataAt(index); } + default byte getRawDataAt(@SuppressWarnings("unused") Object store, int index) { + return getRawDataAt(index); + } + + @SuppressWarnings("unused") + default void setRawDataAt(Object store, int index, byte value) { + throw new UnsupportedOperationException(); + } + + default void setNA(Object store, int index) { + } + RRaw getDataAt(int index); byte getRawDataAt(int index); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractStringVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractStringVector.java index dbe553212e7cad87cf12a565892fa5bf779244da..a30351c95161d9918c6af0f577eecdbf65888f0e 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractStringVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractStringVector.java @@ -32,6 +32,19 @@ public interface RAbstractStringVector extends RAbstractVector { return getDataAt(index); } + default String getDataAt(@SuppressWarnings("unused") Object store, int index) { + return getDataAt(index); + } + + @SuppressWarnings("unused") + default void setDataAt(Object store, int index, String value) { + throw new UnsupportedOperationException(); + } + + default void setNA(Object store, int index) { + setDataAt(store, index, RRuntime.STRING_NA); + } + String getDataAt(int index); RStringVector materialize(); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java index 13e565df325d197d2324bd9ff197d14387b92a57..17a9145be54778440c2ba8fc118dd5c28d442a1d 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java @@ -22,10 +22,12 @@ */ package com.oracle.truffle.r.runtime.data.model; +import com.oracle.truffle.api.interop.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; -public interface RAbstractVector extends RAbstractContainer { +public interface RAbstractVector extends RAbstractContainer, TruffleObject { /** * Creates a copy of the vector. This copies all of the contained data as well. If the data in @@ -72,4 +74,9 @@ public interface RAbstractVector extends RAbstractContainer { void setComplete(boolean complete); + void setNA(Object store, int index); + + default ForeignAccess getForeignAccess() { + return RContext.getEngine().getForeignAccess(this); + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java index 581553fd19a76c1e990ffd6eff49bf597979d1b6..2be05154e78ba3300486103691e224ed10557488 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java @@ -30,6 +30,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RError.RErrorException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.frame.*; @@ -80,8 +81,8 @@ import com.oracle.truffle.r.runtime.env.frame.*; * Multi-tenancy (multiple {@link RContext}s). * <p> * The logic for implementing the three different forms of - * {@link com.oracle.truffle.r.runtime.RContext.Kind} is encapsulated in the {@link #createContext} - * method. + * {@link com.oracle.truffle.r.runtime.context.RContext.ContextKind} is encapsulated in the + * {@link #createContext} method. */ public abstract class REnvironment extends RAttributeStorage implements RTypedValue { @@ -92,22 +93,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa return implicitClass; } - public interface ContextState extends RContext.ContextState { - MaterializedFrame getGlobalFrame(); - - REnvironment getGlobalEnv(); - - Base getBaseEnv(); - - REnvironment getBaseNamespace(); - - REnvironment getNamespaceRegistry(); - - REnvironment.SearchPath getSearchPath(); - - } - - private static class ContextStateImpl implements ContextState { + public static class ContextStateImpl implements RContext.ContextState { private SearchPath searchPath; private final MaterializedFrame globalFrame; private Base baseEnv; @@ -161,21 +147,13 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa this.namespaceRegistry = namespaceRegistry; } - } - - /** - * Since {@REnvironment} is a Truffle value and already has non-zero-arg - * constructors, we define this class as the mechanism for creating the context-specific state. - */ - public static class ClassStateFactory implements RContext.StateFactory { @Override - public ContextState newContext(RContext context, Object... objects) { - return createContext(context, (MaterializedFrame) objects[0]); + public void beforeDestroy(RContext context) { + beforeDestroyContext(context, this); } - @Override - public void beforeDestroy(RContext context, RContext.ContextState state) { - beforeDestroyContext(context, state); + public static ContextStateImpl newContext(RContext context) { + return createContext(context, RRuntime.createNonFunctionFrame().materialize()); } } @@ -247,7 +225,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * Value returned by {@code globalenv()}. */ public static REnvironment globalEnv() { - return RContext.getREnvironmentState().getGlobalEnv(); + return RContext.getInstance().stateREnvironment.getGlobalEnv(); } /** @@ -265,14 +243,14 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * Check whether the given frame is indeed the frame stored in the global environment. */ public static boolean isGlobalEnvFrame(Frame frame) { - return isFrameForEnv(frame, RContext.getREnvironmentState().getGlobalEnv()); + return isFrameForEnv(frame, RContext.getInstance().stateREnvironment.getGlobalEnv()); } /** * Value returned by {@code baseenv()}. This is the "package:base" environment. */ public static REnvironment baseEnv() { - Base baseEnv = RContext.getREnvironmentState().getBaseEnv(); + Base baseEnv = RContext.getInstance().stateREnvironment.getBaseEnv(); assert baseEnv != null; return baseEnv; } @@ -281,7 +259,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * Value set in {@code .baseNameSpaceEnv} variable. This is the "namespace:base" environment. */ public static REnvironment baseNamespaceEnv() { - Base baseEnv = RContext.getREnvironmentState().getBaseEnv(); + Base baseEnv = RContext.getInstance().stateREnvironment.getBaseEnv(); assert baseEnv != null; return baseEnv.getNamespace(); } @@ -301,7 +279,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa public static void baseInitialize(MaterializedFrame baseFrame, MaterializedFrame initialGlobalFrame) { // TODO if namespaceRegistry is ever used in an eval an internal env won't suffice. REnvironment namespaceRegistry = RDataFactory.createInternalEnv(); - ContextStateImpl state = (ContextStateImpl) RContext.getREnvironmentState(); + ContextStateImpl state = RContext.getInstance().stateREnvironment; state.setNamespaceRegistry(namespaceRegistry); Base baseEnv = new Base(baseFrame, initialGlobalFrame); namespaceRegistry.safePut("base", baseEnv.namespaceEnv); @@ -319,10 +297,10 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * path, just replacing the {@code globalenv} component. For a {@code SHARE_PARENT_RO} context * we make shallow copies of the package environments. * - * N.B.Calling {@link RContext#getREnvironmentState()} accesses the new, as yet uninitialized + * N.B. {@link RContext#stateREnvironment} accesses the new, as yet uninitialized * {@link ContextStateImpl} object */ - private static ContextState createContext(RContext context, MaterializedFrame globalFrame) { + private static ContextStateImpl createContext(RContext context, MaterializedFrame globalFrame) { switch (context.getKind()) { case SHARE_PARENT_RW: { /* @@ -330,7 +308,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * parent of the previous global env. Then we create a copy of the SearchPath and * patch the global entry. */ - ContextStateImpl parentState = (ContextStateImpl) context.getParent().getThisContextState(RContext.ClassStateKind.REnvironment); + ContextStateImpl parentState = context.getParent().stateREnvironment; Base parentBaseEnv = parentState.getBaseEnv(); NSBaseMaterializedFrame nsBaseFrame = (NSBaseMaterializedFrame) parentBaseEnv.namespaceEnv.frameAccess.getFrame(); MaterializedFrame prevGlobalFrame = RArguments.getEnclosingFrame(nsBaseFrame); @@ -348,7 +326,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa case SHARE_PARENT_RO: { /* We make shallow copies of all the default package environments in the parent */ - ContextStateImpl parentState = (ContextStateImpl) context.getParent().getThisContextState(RContext.ClassStateKind.REnvironment); + ContextStateImpl parentState = context.getParent().stateREnvironment; SearchPath parentSearchPath = parentState.getSearchPath(); // clone all the environments below global from the parent REnvironment e = parentSearchPath.get(1).cloneEnv(globalFrame); @@ -390,7 +368,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa */ MaterializedFrame parentGlobalFrame = ((ContextStateImpl) state).parentGlobalFrame; Global parentGlobalEnv = (Global) RArguments.getEnvironment(parentGlobalFrame); - ContextStateImpl parentState = (ContextStateImpl) context.getParent().getThisContextState(RContext.ClassStateKind.REnvironment); + ContextStateImpl parentState = context.getParent().stateREnvironment; NSBaseMaterializedFrame nsBaseFrame = (NSBaseMaterializedFrame) parentState.baseEnv.namespaceEnv.frameAccess.getFrame(); nsBaseFrame.updateGlobalFrame(parentGlobalFrame); parentState.baseEnv.safePut(".GlobalEnv", parentGlobalEnv); @@ -464,7 +442,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * Data for the {@code search} function. */ public static String[] searchPath() { - SearchPath searchPath = RContext.getREnvironmentState().getSearchPath(); + SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath(); String[] result = new String[searchPath.size()]; for (int i = 0; i < searchPath.size(); i++) { REnvironment env = searchPath.get(i); @@ -480,7 +458,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * @return the environment or {@code null} if not found. */ public static REnvironment lookupOnSearchPath(String name) { - SearchPath searchPath = RContext.getREnvironmentState().getSearchPath(); + SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath(); int i = lookupIndexOnSearchPath(name); return i <= 0 ? null : searchPath.get(i - 1); } @@ -492,7 +470,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * @return the index (1-based) or {@code 0} if not found. */ public static int lookupIndexOnSearchPath(String name) { - SearchPath searchPath = RContext.getREnvironmentState().getSearchPath(); + SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath(); for (int i = 0; i < searchPath.size(); i++) { REnvironment env = searchPath.get(i); String searchName = env.getSearchName(); @@ -504,11 +482,11 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa } public static REnvironment getNamespaceRegistry() { - return RContext.getREnvironmentState().getNamespaceRegistry(); + return RContext.getInstance().stateREnvironment.getNamespaceRegistry(); } public static void registerNamespace(String name, REnvironment env) { - RContext.getREnvironmentState().getNamespaceRegistry().safePut(name, env); + RContext.getInstance().stateREnvironment.getNamespaceRegistry().safePut(name, env); } /** @@ -517,7 +495,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * namespace. */ public static REnvironment getRegisteredNamespace(String name) { - return (REnvironment) RContext.getREnvironmentState().getNamespaceRegistry().get(name); + return (REnvironment) RContext.getInstance().stateREnvironment.getNamespaceRegistry().get(name); } /** @@ -531,7 +509,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa assert pos >= 2; // N.B. pos is 1-based int bpos = pos - 1; - SearchPath searchPath = RContext.getREnvironmentState().getSearchPath(); + SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath(); if (bpos > searchPath.size() - 1) { bpos = searchPath.size() - 1; } @@ -562,7 +540,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * @return the {@link REnvironment} that was detached. */ public static REnvironment detach(int pos) throws DetachException { - SearchPath searchPath = RContext.getREnvironmentState().getSearchPath(); + SearchPath searchPath = RContext.getInstance().stateREnvironment.getSearchPath(); if (pos == searchPath.size()) { detachException(RError.Message.ENV_DETACH_BASE); } @@ -676,7 +654,7 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa * "namespace" env. */ public REnvironment getPackageNamespaceEnv() { - if (this == RContext.getREnvironmentState().getBaseEnv()) { + if (this == RContext.getInstance().stateREnvironment.getBaseEnv()) { return ((Base) this).namespaceEnv; } String envName; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java index f687cd54027dc92e7ee35be0299171eac675cca3..0fd93d267537a7914ef4a9144b1113ac042f5859 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java @@ -18,6 +18,7 @@ import java.util.concurrent.atomic.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RError.RErrorException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; /** diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java index b2fc96982c974d578d2fbc3ddf503a7a5c9f3c61..9c1cd75dff5485aa4db38131984c24904303aa69 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContextStateFactory.java @@ -22,31 +22,22 @@ */ package com.oracle.truffle.r.runtime.ffi; -import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.RContext.ContextState; /** * This is the factory-independent class referenced by {@link RContext} that manages the * context-specific state for any given {@link RFFIFactory}. It simply forwards the calls to the * actual factory. */ -public class RFFIContextStateFactory implements StateFactory { +public class RFFIContextStateFactory { private static RFFIFactory theFactory; public static void registerFactory(RFFIFactory factory) { theFactory = factory; } - public ContextState newContext(RContext context, Object... objects) { - return theFactory.newContext(context, objects); + public static ContextState newContext(RContext context) { + return theFactory.newContext(context); } - - public void systemInitialized(RContext context, ContextState state) { - theFactory.systemInitialized(context, state); - } - - public void beforeDestroy(RContext context, ContextState state) { - theFactory.beforeDestroy(context, state); - } - } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java index a5bd96357a8f49e74a8afbf5c6139e6c5221934d..0eb95d6bc7155e95a03b5e402788b4f26bd35ecc 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java @@ -26,17 +26,18 @@ import java.io.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.RContext.*; /** * Factory class for the different possible implementations of the {@link RFFI} interface. The * choice of factory is made by the R engine and set here by the call to {@link #setRFFIFactory}. * * The RFFI may need to do special things in the case of multiple contexts, hence any given factory - * must support the {@link com.oracle.truffle.r.runtime.RContext.StateFactory} interface. However, - * since we don't know exactly which factory will be used, {@link RContext} references the - * {@link RFFIContextStateFactory} class. + * must support the {@link #newContext(RContext)} method. However, since we don't know exactly which + * factory will be used, {@link RContext} references the {@link RFFIContextStateFactory} class. */ -public abstract class RFFIFactory implements RContext.StateFactory { +public abstract class RFFIFactory { protected static RFFI theRFFI; @@ -103,4 +104,5 @@ public abstract class RFFIFactory implements RContext.StateFactory { throw new IOException(errMsg); } + public abstract ContextState newContext(RContext context); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java index 2bd8b5b3391b72b336d03a9556b26c1caa139c3d..8f447a37bf711415ef56d90568b22996120a1275 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RBaseNode.java @@ -27,6 +27,7 @@ import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.runtime.RDeparse.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.env.*; /** diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java index fe05efa00548d9b36e67df3b8b4bf3f10bb4899f..5c1152172b7a10324e2e5728e4e869a379ce4406 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxNode.java @@ -24,7 +24,7 @@ package com.oracle.truffle.r.runtime.nodes; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; /** * An interface that identifies an AST node as being part of the syntactic structure of the diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/na/NACheck.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/na/NACheck.java index abacaffd589fc0ff3118f29e3ac27c79b6d792c6..b8e6ef30c03c92061a18f032ebad9022c6f22080 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/na/NACheck.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/na/NACheck.java @@ -77,7 +77,7 @@ public final class NACheck { } } - public void enable(RAbstractVector value) { + public void enable(RAbstractContainer value) { if (state == NO_CHECK) { enable(!value.isComplete()); } @@ -111,6 +111,13 @@ public final class NACheck { return false; } + public void seenNA() { + if (state != CHECK) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + state = CHECK; + } + } + public boolean check(int value) { if (state != NO_CHECK && isNA(value)) { if (state == CHECK_DEOPT) { @@ -144,6 +151,17 @@ public final class NACheck { return false; } + public boolean checkListElement(Object value) { + if (state != NO_CHECK && value == RNull.instance) { + if (state == CHECK_DEOPT) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + state = CHECK; + } + return true; + } + return false; + } + public int convertLogicalToInt(byte value) { if (check(value)) { return RRuntime.INT_NA; @@ -181,8 +199,13 @@ public final class NACheck { return RDataFactory.createComplex(value, 0); } + public boolean isEnabled() { + return state != NO_CHECK; + } + public boolean neverSeenNA() { - // need to check for both NA and NaN (the latter used for double to int conversions) + // need to check for both NA and NaN (the latter used for double to int + // conversions) return state != CHECK && !seenNaN; } @@ -302,7 +325,7 @@ public final class NACheck { return result; } - public int[] convertDoubleVectorToIntData(RDoubleVector vector) { + public int[] convertDoubleVectorToIntData(RAbstractDoubleVector vector) { int length = vector.getLength(); int[] result = new int[length]; boolean warning = false; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java index 6a2d72df06ce9917e87043ca664bfaf9eb2332b9..53e9d2ca1fffe4e35710a6451e6f032239738e50 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java @@ -11,11 +11,12 @@ */ package com.oracle.truffle.r.runtime.rng; -import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.r.runtime.*; -import com.oracle.truffle.r.runtime.RContext.ContextState; import com.oracle.truffle.r.runtime.RError.RErrorException; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.runtime.data.*; import com.oracle.truffle.r.runtime.env.*; import com.oracle.truffle.r.runtime.ffi.*; @@ -35,7 +36,7 @@ import com.oracle.truffle.r.runtime.rng.user.*; * we do create/update it when the seed/kind is changed, primarily as a debugging aid. N.B. GnuR * updates it on <i>every</i> random number generation! */ -public class RRNG implements RContext.StateFactory { +public class RRNG { /** * The standard kinds provided by GnuR, where the ordinal value corresponds to the argument to * {@link RRNG#doSetSeed}. @@ -146,7 +147,7 @@ public class RRNG implements RContext.StateFactory { } } - private static final class ContextStateImpl implements RContext.ContextState { + public static final class ContextStateImpl implements RContext.ContextState { private Kind currentKind; private NormKind currentNormKind; @@ -159,24 +160,23 @@ public class RRNG implements RContext.StateFactory { currentKind = kind; } - @SuppressWarnings("unused") void updateCurrentNormKind(NormKind normKind) { currentNormKind = normKind; } - } - public ContextState newContext(RContext context, Object... objects) { - int seed = timeToSeed(); - try { - initGenerator(DEFAULT_KIND.setGenerator(), seed); - } catch (RNGException ex) { - Utils.fail("failed to initialize default random number generator"); + public static ContextStateImpl newContext(@SuppressWarnings("unused") RContext context) { + int seed = timeToSeed(); + try { + initGenerator(DEFAULT_KIND.setGenerator(), seed); + } catch (RNGException ex) { + Utils.fail("failed to initialize default random number generator"); + } + return new ContextStateImpl(DEFAULT_KIND, DEFAULT_NORM_KIND); } - return new ContextStateImpl(DEFAULT_KIND, DEFAULT_NORM_KIND); } private static ContextStateImpl getContextState() { - return (ContextStateImpl) RContext.getContextState(RContext.ClassStateKind.RNG); + return RContext.getInstance().stateRNG; } public static int currentKindAsInt() { diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index f4b8ca54b61a6c3c27342c4bedd8c2d269b74813..15c294106f6470a210604ae2d924030a51f386a2 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -49446,10 +49446,6 @@ $x #{ x<-7; f<-function() x<<-42; f_copy<-as.list(environment())[["f"]]; f_copy(); x } [1] 42 -##com.oracle.truffle.r.test.builtins.TestMiscBuiltins.testChannels -#{ if (length(grep("FastR", R.Version()$version.string)) == 1) { ch <- fastr.channel.create(1L); cx <- fastr.context.create("SHARED_NOTHING"); fastr.context.spawn(cx, "ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1]<-7; fastr.channel.send(ch, x)"); y<-c(42); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) } else { print(c(7L, 42L)) } } -[1] 7 42 - ##com.oracle.truffle.r.test.builtins.TestMiscBuiltins.testDataFrameTypeCheck #is.data.frame("1") [1] FALSE diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java index 55b15d8a1ecc036259556cbabb7660bc7eb71b98..9b356f5ad18a09a0d0135d9bc4f1795e6b2e7236 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java @@ -312,6 +312,8 @@ public class TestBase { protected static final String ERROR = "Error"; protected static final String WARNING = "Warning message"; + private static final boolean IGNORE_ERROR_COMPARISON = false; + /** * If this is set to {@code true}, {@link Output#ContainsError} will compare the full output * instead of truncating leading "Error" strings and such. This means it will behave like @@ -352,6 +354,11 @@ public class TestBase { // empty } + // support testing of FastR-only functionality (equivalent GNU R output provided separately) + protected void assertEvalFastR(String input, String gnuROutput) { + evalAndCompare(new String[]{"if (length(grep(\"FastR\", R.Version()$version.string)) != 1) { " + gnuROutput + " } else " + input}); + } + /* * implementation support methods */ @@ -425,27 +432,12 @@ public class TestBase { } else { String result = fastREval(input); - boolean ok; - if (expected.equals(result) || searchWhiteLists(whiteLists, input, expected, result)) { - ok = true; - } else { - if (containsWarning || (mayContainWarning && expected.contains(WARNING))) { - String resultWarning = getWarningMessage(result); - String expectedWarning = getWarningMessage(expected); - ok = resultWarning.equals(expectedWarning); - result = getOutputWithoutWarning(result); - expected = getOutputWithoutWarning(expected); - } else { - ok = true; - } - if (ok) { - if (containsError || (mayContainError && expected.startsWith(ERROR))) { - ok = result.startsWith(ERROR) && checkMessageStripped(expected, result); - } else { - ok = expected.equals(result); - } - } - } + CheckResult checkResult = checkResult(whiteLists, input, expected, result, containsWarning, mayContainWarning, containsError, mayContainError); + + result = checkResult.result; + expected = checkResult.expected; + boolean ok = checkResult.ok; + if (ProcessFailedTests) { if (ok) { unexpectedSuccessfulMicroTests.add(getTestContext() + ": " + input); @@ -488,15 +480,64 @@ public class TestBase { } } - private static boolean searchWhiteLists(WhiteList[] whiteLists, String input, String expected, String result) { + private static class CheckResult { + + public final boolean ok; + public final String result; + public final String expected; + + public CheckResult(boolean ok, String result, String expected) { + this.ok = ok; + this.result = result; + this.expected = expected; + } + + } + + @SuppressWarnings("unused") + private CheckResult checkResult(WhiteList[] whiteLists, String input, String originalExpected, String originalResult, boolean containsWarning, boolean mayContainWarning, boolean containsError, + boolean mayContainError) { + boolean ok; + String result = originalResult; + String expected = originalExpected; + if (expected.equals(result) || searchWhiteLists(whiteLists, input, expected, result, containsWarning, mayContainWarning, containsError, mayContainError)) { + ok = true; + } else { + if (containsWarning || (mayContainWarning && expected.contains(WARNING))) { + String resultWarning = getWarningMessage(result); + String expectedWarning = getWarningMessage(expected); + ok = resultWarning.equals(expectedWarning); + result = getOutputWithoutWarning(result); + expected = getOutputWithoutWarning(expected); + } else { + ok = true; + } + if (ok) { + if (containsError || (mayContainError && expected.startsWith(ERROR))) { + ok = result.startsWith(ERROR) && (IGNORE_ERROR_COMPARISON || checkMessageStripped(expected, result)); + } else { + ok = expected.equals(result); + } + } + } + return new CheckResult(ok, result, expected); + } + + private boolean searchWhiteLists(WhiteList[] whiteLists, String input, String expected, String result, boolean containsWarning, boolean mayContainWarning, boolean containsError, + boolean mayContainError) { + if (whiteLists == null) { + return false; + } for (WhiteList list : whiteLists) { WhiteList.Results wlr = list.get(input); if (wlr != null) { - if (!wlr.expected.equals(expected)) { + CheckResult checkedResult = checkResult(null, input, wlr.expected, expected, containsWarning, mayContainWarning, containsError, mayContainError); + if (!checkedResult.ok) { System.out.println("expected output does not match: " + wlr.expected + " vs. " + expected); return false; } - if (wlr.fastR.equals(result)) { + CheckResult fastRResult = checkResult(null, input, wlr.fastR, result, containsWarning, mayContainWarning, containsError, mayContainError); + if (fastRResult.ok) { list.markUsed(input); return true; } @@ -559,24 +600,11 @@ public class TestBase { if (cxr < 0 || cxe < 0) { return false; } - String resultStripped = stripWhitespace(result, cxr + 1); - String expectedStripped = stripWhitespace(expected, cxe + 1); + String resultStripped = result.substring(cxr + 1).trim(); + String expectedStripped = expected.substring(cxe + 1).trim(); return resultStripped.equals(expectedStripped); } - private static String stripWhitespace(String r, int ix) { - int x = ix; - int rl = r.length(); - char ch = r.charAt(x); - if (Character.isWhitespace(ch)) { - while (Character.isWhitespace(ch) && x < rl) { - ch = r.charAt(x++); - } - x--; - } - return r.substring(x); - } - /** * Evaluate {@code input} in FastR, returning all (virtual) console output that was produced. */ diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Sysgetenv.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Sysgetenv.java index cb24b6620433a519ae4833efc6e637630655760d..6d0e81bd0856c09df543b4ff5170e2d07c83848e 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Sysgetenv.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Sysgetenv.java @@ -12,7 +12,6 @@ package com.oracle.truffle.r.test.builtins; import org.junit.*; -import com.oracle.truffle.r.runtime.*; import com.oracle.truffle.r.test.*; // Checkstyle: stop line length check @@ -32,10 +31,7 @@ public class TestBuiltin_Sysgetenv extends TestBase { public void testEnvVars() { assertEval(Output.ContainsError, "{ Sys.setenv(\"a\") } "); assertEval("{ Sys.setenv(FASTR_A=\"a\"); Sys.getenv(\"FASTR_A\"); } "); - REnvVars.unset("FASTR_A"); assertEval("{ Sys.setenv(FASTR_A=\"a\", FASTR_B=\"b\"); Sys.getenv(c(\"FASTR_A\", \"FASTR_B\")); } "); - REnvVars.unset("FASTR_A"); - REnvVars.unset("FASTR_B"); assertEval("{ Sys.getenv(\"FASTR_A\") } "); assertEval("{ Sys.getenv(\"FASTR_A\", unset=\"UNSET\") } "); } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java index de5c5c41adb057874d1c8bed7d057b6734321ca7..ab4cc83be5d435134eaa6dd16576ca30192348cb 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestMiscBuiltins.java @@ -346,8 +346,4 @@ public class TestMiscBuiltins extends TestBase { assertEval("{ x<-as.character(list(a=\"0\", b=\"0\", c=\"0.3\")); type.convert(x, as.is=FALSE) }"); } - @Test - public void testChannels() { - assertEval("{ if (length(grep(\"FastR\", R.Version()$version.string)) == 1) { ch <- fastr.channel.create(1L); cx <- fastr.context.create(\"SHARED_NOTHING\"); fastr.context.spawn(cx, \"ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1]<-7; fastr.channel.send(ch, x)\"); y<-c(42); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) } else { print(c(7L, 42L)) } }"); - } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java index c2f552450c629d4d84966843ad96197e5187acdf..a0f032e2697c9aa0d58366e3b5148a6a8b9cb1c2 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java @@ -26,9 +26,13 @@ import java.util.*; import java.util.concurrent.*; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.vm.*; import com.oracle.truffle.r.engine.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.RCmdOptions.Client; +import com.oracle.truffle.r.runtime.context.*; +import com.oracle.truffle.r.runtime.context.Engine.ParseException; +import com.oracle.truffle.r.runtime.context.RContext.ContextKind; public final class FastRSession implements RSession { @@ -38,7 +42,7 @@ public final class FastRSession implements RSession { * A (virtual) console handler that collects the output in a {@link StringBuilder} for * comparison. It does not separate error output as the test analysis doesn't need it. */ - private static class ConsoleHandler implements RContext.ConsoleHandler { + public static class TestConsoleHandler implements ConsoleHandler { private final StringBuilder buffer = new StringBuilder(); @TruffleBoundary @@ -91,13 +95,17 @@ public final class FastRSession implements RSession { return RContext.CONSOLE_WIDTH; } + public String getInputDescription() { + return "<test input>"; + } } - private static ConsoleHandler consoleHandler; private static FastRSession singleton; + private final TestConsoleHandler consoleHandler; + private final TruffleVM main; + private EvalThread evalThread; - private final RContext main; public static FastRSession create() { if (singleton == null) { @@ -106,18 +114,19 @@ public final class FastRSession implements RSession { return singleton; } - public RContext createTestContext() { + public TruffleVM createTestContext() { create(); - RContext context = RContextFactory.createShareParentReadWrite(main, new String[0], consoleHandler).activate(); - context.setSystemTimeZone(TimeZone.getTimeZone("CET")); - return context; + RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, new String[0]); + ContextInfo info = ContextInfo.create(options, ContextKind.SHARE_PARENT_RW, RContext.fromTruffleVM(main), consoleHandler, TimeZone.getTimeZone("CET")); + return RContextFactory.create(info); } private FastRSession() { - consoleHandler = new ConsoleHandler(); - RContextFactory.initialize(); + consoleHandler = new TestConsoleHandler(); try { - main = RContextFactory.createInitial(new String[0], consoleHandler).activate(); + RCmdOptions options = RCmdOptions.parseArguments(Client.RSCRIPT, new String[0]); + ContextInfo info = ContextInfo.create(options, ContextKind.SHARE_NOTHING, null, consoleHandler); + main = RContextFactory.create(info); } finally { System.out.print(consoleHandler.buffer.toString()); } @@ -179,16 +188,22 @@ public final class FastRSession implements RSession { break; } try { - RContext testContext = createTestContext(); + TruffleVM vm = createTestContext(); try { - testContext.getThisEngine().parseAndEvalTest(Source.fromText(expression, "<test_input>"), true, false); + vm.eval(TruffleRLanguage.MIME, expression); } finally { - testContext.destroy(); + RContext.destroyContext(vm); } + } catch (ParseException e) { + e.report(consoleHandler); } catch (RError e) { // nothing to do } catch (Throwable t) { - killedByException = t; + if (t.getCause() instanceof RError) { + // nothing to do + } else { + killedByException = t; + } } finally { exit.release(); } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestChannels.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestChannels.java new file mode 100644 index 0000000000000000000000000000000000000000..887abbc4497a1c66b062715bb4b4164978feb814 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestChannels.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.test.library.fastr; + +import org.junit.*; + +import com.oracle.truffle.r.test.*; + +public class TestChannels extends TestBase { + + @Test + public void testChannels() { + assertEvalFastR("{ ch <- fastr.channel.create(1L); cx <- fastr.context.create(\"SHARED_NOTHING\"); fastr.context.spawn(cx, \"ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1]<-7; fastr.channel.send(ch, x)\"); y<-c(42); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) }", + "print(c(7L, 42L))"); + assertEvalFastR("{ ch <- fastr.channel.create(1L); cx <- fastr.context.create(\"SHARED_NOTHING\"); fastr.context.spawn(cx, \"ch <- fastr.channel.get(1L); x<-fastr.channel.receive(ch); x[1][1]<-7; fastr.channel.send(ch, x)\"); y<-list(c(42)); fastr.channel.send(ch, y); x<-fastr.channel.receive(ch); fastr.context.join(cx); fastr.channel.close(ch); print(c(x,y)) }", + "print(list(7L, 42L))"); + } +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java index 888ee70a6a966fad78aebf38b40a5b2aff6c4edf..455976bfe1c71f411e2c07c711550a033339a26c 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java @@ -31,6 +31,7 @@ import java.util.*; import org.junit.*; import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.context.*; import com.oracle.truffle.r.test.*; /** @@ -107,7 +108,7 @@ public class TestRPackages extends TestBase { Map<String, String> env = pb.environment(); env.put("R_LIBS_USER", rpackagesLibs.toString()); if (!generatingExpected()) { - env.put("R_INSTALL_TAR", REnvVars.get("TAR")); + env.put("R_INSTALL_TAR", RContext.getInstance().stateREnvVars.get("TAR")); } try { if (FastROptions.debugMatches("TestRPackages")) { diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1a1aa5f9b72e19a8e3d505cff6aadf8f134d33cb --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.test.tck; + +import com.oracle.truffle.tck.TruffleTCK; +import com.oracle.truffle.api.vm.TruffleVM; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class FastRTckTest extends TruffleTCK { + @Test + public void testVerifyPresence() { + TruffleVM vm = TruffleVM.newVM().build(); + assertTrue("Our language is present", vm.getLanguages().containsKey("text/x-r")); + } + + @Override + protected TruffleVM prepareVM() throws Exception { + TruffleVM vm = TruffleVM.newVM().build(); + // @formatter:off + vm.eval("text/x-r", + "fourtyTwo <- function() {\n" + + " 42L\n" + + "}\n" + + "Interop.export('fourtyTwo', fourtyTwo)\n" + + "plus <- function(a, b) {\n" + + " a + b\n" + + "}\n" + + "Interop.export('plus', plus)\n" + + "apply <- function(f) {\n" + + " f(18L, 32L) + 10L\n" + + "}\n" + + "Interop.export('apply', apply)\n" + + "null <- function() {\n" + + " NULL\n" + + "}\n" + + "Interop.export('null', null)\n" + + "counter <- 0L\n" + + "count <- function() {\n" + + " counter <<- counter + 1L\n" + + "}\n" + + "Interop.export('count', count)\n" + ); + // @formatter:on + return vm; + } + + @Override + protected String mimeType() { + return "text/x-r"; + } + + @Override + protected String fourtyTwo() { + return "fourtyTwo"; + } + + @Override + protected String plusInt() { + return "plus"; + } + + @Override + protected String returnsNull() { + return "null"; + } + + @Override + protected String applyNumbers() { + return "apply"; + } + + @Override + protected String countInvocations() { + return "count"; + } + + @Override + protected String invalidCode() { + // @formatter:off + return + "main <- f unction() {\n" + + " re turn(42)\n" + + "}\n"; + // @formatter:on + } + + @Override + @Test + public void testNull() { + // disabled because we don't provide a Java "null" value in R + } +} diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index 6a5eedd0b1df9fa688295021a2252c9c7ff8a1ca..c04e8cf7e6dbd8949306a52c05cc23a69c6b98d2 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -19,8 +19,6 @@ com.oracle.truffle.r.native/builtinlibs/src/rdummy.c,no.copyright com.oracle.truffle.r.native/fficall/jni/src/register.c,gnu_r.copyright com.oracle.truffle.r.native/fficall/jni/src/alloc.c,gnu_r.copyright com.oracle.truffle.r.native/include/jni/src/libintl.h,no.copyright -com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsDevice.h,no.copyright -com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsEngine.h,no.copyright com.oracle.truffle.r.native/library/base/src/registration.c,no.copyright com.oracle.truffle.r.native/library/graphics/src/graphics.h,no.copyright com.oracle.truffle.r.native/library/graphics/src/init.c,no.copyright @@ -41,6 +39,22 @@ com.oracle.truffle.r.native/library/tools/src/init.c,no.copyright com.oracle.truffle.r.native/library/tools/src/tools.h,no.copyright com.oracle.truffle.r.native/library/utils/src/init.c,no.copyright com.oracle.truffle.r.native/library/utils/src/utils.h,no.copyright +com.oracle.truffle.r.native/library/grid/src/gpar.c no.copyright +com.oracle.truffle.r.native/library/grid/src/grid.c no.copyright +com.oracle.truffle.r.native/library/grid/src/grid.h no.copyright +com.oracle.truffle.r.native/library/grid/src/just.c no.copyright +com.oracle.truffle.r.native/library/grid/src/layout.c no.copyright +com.oracle.truffle.r.native/library/grid/src/matrix.c no.copyright +com.oracle.truffle.r.native/library/grid/src/register.c no.copyright +com.oracle.truffle.r.native/library/grid/src/state.c no.copyright +com.oracle.truffle.r.native/library/grid/src/unit.c no.copyright +com.oracle.truffle.r.native/library/grid/src/util.c no.copyright +com.oracle.truffle.r.native/library/grid/src/viewport.c no.copyright +com.oracle.truffle.r.native/library/parallel/src/glpi.h has missing copyright +com.oracle.truffle.r.native/library/parallel/src/init.c no.copyright +com.oracle.truffle.r.native/library/parallel/src/parallel.h no.copyright +com.oracle.truffle.r.native/library/parallel/src/rngstream.c no.copyright +com.oracle.truffle.r.native/library/splines/src/splines.c no.copyright com.oracle.truffle.r.native/run/R.sh,oracle_bash.copyright com.oracle.truffle.r.native/run/Rscript_exec.sh,oracle_bash.copyright com.oracle.truffle.r.native/run/Rscript.sh,oracle_bash.copyright