Mercurial > hg > graal-compiler
diff graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyContext.java @ 13514:0fbee3eb71f0
Ruby: import project.
author | Chris Seaton <chris.seaton@oracle.com> |
---|---|
date | Mon, 06 Jan 2014 17:12:09 +0000 |
parents | |
children | fb846424299f |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyContext.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +import java.math.*; +import java.util.concurrent.atomic.*; + +import jnr.posix.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.debug.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; +import com.oracle.truffle.ruby.runtime.subsystems.*; + +/** + * The global state of a running Ruby system. + */ +public class RubyContext implements ExecutionContext { + + private final Configuration configuration; + private final RubyParser parser; + private final CoreLibrary coreLibrary; + private final FeatureManager featureManager; + private final ObjectSpaceManager objectSpaceManager; + private final TraceManager traceManager; + private final ThreadManager threadManager; + private final FiberManager fiberManager; + private final AtExitManager atExitManager; + private final RubyDebugManager debugManager; + private final SourceManager sourceManager; + + private AtomicLong nextObjectID = new AtomicLong(0); + + private String currentDirectory = System.getProperty("user.dir"); + + private POSIX posix = POSIXFactory.getPOSIX(); + + public RubyContext(RubyParser parser) { + this(new Configuration(new ConfigurationBuilder()), parser); + } + + public RubyContext(Configuration configuration, RubyParser parser) { + assert configuration != null; + + this.configuration = configuration; + this.parser = parser; + + objectSpaceManager = new ObjectSpaceManager(this); + traceManager = new TraceManager(this); + + // See note in CoreLibrary#initialize to see why we need to break this into two statements + coreLibrary = new CoreLibrary(this); + coreLibrary.initialize(); + + featureManager = new FeatureManager(this); + atExitManager = new AtExitManager(); + sourceManager = new SourceManager(); + + debugManager = configuration.getDebug() ? new RubyDebugManager(this) : null; + + // Must initialize threads before fibers + + threadManager = new ThreadManager(this); + + if (configuration.getRubyVersion().is19OrLater()) { + fiberManager = new FiberManager(this); + } else { + fiberManager = null; + } + } + + public String getLanguageShortName() { + return configuration.getRubyVersion().getShortName(); + } + + public RubyDebugManager getDebugManager() { + return debugManager; + } + + public void implementationMessage(String format, Object... arguments) { + System.err.println("rubytruffle: " + String.format(format, arguments)); + } + + public void load(Source source) { + execute(this, source, RubyParser.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null); + } + + public void loadFile(String fileName) { + final Source source = sourceManager.get(fileName); + final String code = source.getCode(); + if (code == null) { + throw new RuntimeException("Can't read file " + fileName); + } + execute(this, source, RubyParser.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null); + } + + /** + * Receives runtime notification that execution has halted. + */ + public void haltedAt(Node node, MaterializedFrame frame) { + runShell(node, frame); + } + + public Object eval(String code) { + final Source source = sourceManager.get("(eval)", code); + return execute(this, source, RubyParser.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null); + } + + public Object eval(String code, RubyModule module) { + final Source source = sourceManager.get("(eval)", code); + return execute(this, source, RubyParser.ParserContext.MODULE, module, null); + } + + public Object eval(String code, RubyBinding binding) { + final Source source = sourceManager.get("(eval)", code); + return execute(this, source, RubyParser.ParserContext.TOP_LEVEL, binding.getSelf(), binding.getFrame()); + } + + public void runShell(Node node, MaterializedFrame frame) { + MaterializedFrame existingLocals = frame; + + String prompt = "Ruby> "; + if (node != null) { + final SourceSection src = node.getSourceSection(); + if (src != null) { + prompt = (src.getSource().getName() + ":" + src.getStartLine() + "> "); + } + } + + while (true) { + try { + final String line = configuration.getInputReader().readLine(prompt); + + final ShellResult result = evalShell(line, existingLocals); + + configuration.getStandardOut().println("=> " + result.getResult()); + + existingLocals = result.getFrame(); + } catch (BreakShellException e) { + return; + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public ShellResult evalShell(String code, MaterializedFrame existingLocals) { + final Source source = sourceManager.get("(shell)", code); + return (ShellResult) execute(this, source, RubyParser.ParserContext.SHELL, coreLibrary.getMainObject(), existingLocals); + } + + public Object execute(RubyContext context, Source source, RubyParser.ParserContext parserContext, Object self, MaterializedFrame parentFrame) { + if (configuration.getPrintExecutedFiles()) { + implementationMessage("executing: %s", source.getName()); + } + + try { + final RubyParserResult parseResult = parser.parse(context, source, parserContext, parentFrame); + final RubyArguments arguments = new RubyArguments(parentFrame, self, null); + final CallTarget callTarget = Truffle.getRuntime().createCallTarget(parseResult.getRootNode(), parseResult.getFrameDescriptor()); + + return callTarget.call(null, arguments); + } catch (RaiseException e) { + throw e; + } catch (ThrowException e) { + if (context.getConfiguration().getRubyVersion().is18OrEarlier()) { + throw new RaiseException(context.getCoreLibrary().nameErrorUncaughtThrow(e.getTag())); + } else { + throw new RaiseException(context.getCoreLibrary().argumentErrorUncaughtThrow(e.getTag())); + } + } catch (BreakShellException | QuitException e) { + throw e; + } catch (Throwable e) { + throw new RaiseException(ExceptionTranslator.translateException(this, e)); + } + } + + public long getNextObjectID() { + // TODO(CS): We can theoretically run out of long values + + final long id = nextObjectID.getAndIncrement(); + + if (id < 0) { + nextObjectID.set(Long.MIN_VALUE); + throw new RuntimeException("Object IDs exhausted"); + } + + return id; + } + + public void shutdown() { + atExitManager.run(); + + threadManager.leaveGlobalLock(); + + objectSpaceManager.shutdown(); + + if (fiberManager != null) { + fiberManager.shutdown(); + } + } + + public RubyString makeString(String string) { + return new RubyString(coreLibrary.getStringClass(), string); + } + + public RubyString makeString(char string) { + return makeString(Character.toString(string)); + } + + public Configuration getConfiguration() { + return configuration; + } + + public CoreLibrary getCoreLibrary() { + return coreLibrary; + } + + public FeatureManager getFeatureManager() { + return featureManager; + } + + public ObjectSpaceManager getObjectSpaceManager() { + return objectSpaceManager; + } + + public TraceManager getTraceManager() { + return traceManager; + } + + public FiberManager getFiberManager() { + return fiberManager; + } + + public ThreadManager getThreadManager() { + return threadManager; + } + + public RubyParser getParser() { + return parser; + } + + /** + * Utility method to check if an object should be visible in a Ruby program. Used in assertions + * at method boundaries to check that only values we want to be visible to the programmer become + * so. + */ + public static boolean shouldObjectBeVisible(Object object) { + // TODO(cs): RubyMethod should never be visible + + return object instanceof UndefinedPlaceholder || // + object instanceof Boolean || // + object instanceof Integer || // + object instanceof BigInteger || // + object instanceof Double || // + object instanceof RubyBasicObject || // + object instanceof RubyMethod || // + object instanceof NilPlaceholder || // + object instanceof RubyMethod; + } + + public static boolean shouldObjectsBeVisible(Object... objects) { + for (Object object : objects) { + if (!shouldObjectBeVisible(object)) { + return false; + } + } + + return true; + } + + public void setCurrentDirectory(String currentDirectory) { + this.currentDirectory = currentDirectory; + } + + public String getCurrentDirectory() { + return currentDirectory; + } + + public POSIX getPOSIX() { + return posix; + } + + public AtExitManager getAtExitManager() { + return atExitManager; + } + + public SourceManager getSourceManager() { + return sourceManager; + } + +}