org.jruby.truffle.RubyContext Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2013, 2017 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 org.jruby.truffle;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerOptions;
import com.oracle.truffle.api.ExecutionContext;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleOptions;
import com.oracle.truffle.api.instrumentation.Instrumenter;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.builtins.PrimitiveManager;
import org.jruby.truffle.core.CoreLibrary;
import org.jruby.truffle.core.CoreMethods;
import org.jruby.truffle.core.encoding.EncodingManager;
import org.jruby.truffle.core.exception.CoreExceptions;
import org.jruby.truffle.core.kernel.AtExitManager;
import org.jruby.truffle.core.kernel.TraceManager;
import org.jruby.truffle.core.module.ModuleOperations;
import org.jruby.truffle.core.objectspace.ObjectSpaceManager;
import org.jruby.truffle.core.rope.RopeTable;
import org.jruby.truffle.core.string.CoreStrings;
import org.jruby.truffle.core.string.FrozenStrings;
import org.jruby.truffle.core.symbol.SymbolTable;
import org.jruby.truffle.core.thread.ThreadManager;
import org.jruby.truffle.interop.InteropManager;
import org.jruby.truffle.language.CallStackManager;
import org.jruby.truffle.language.LexicalScope;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.SafepointManager;
import org.jruby.truffle.language.arguments.RubyArguments;
import org.jruby.truffle.language.control.JavaException;
import org.jruby.truffle.language.loader.CodeLoader;
import org.jruby.truffle.language.loader.FeatureLoader;
import org.jruby.truffle.language.loader.SourceLoader;
import org.jruby.truffle.language.methods.DeclarationContext;
import org.jruby.truffle.language.methods.InternalMethod;
import org.jruby.truffle.language.objects.shared.SharedObjects;
import org.jruby.truffle.options.Options;
import org.jruby.truffle.options.OptionsBuilder;
import org.jruby.truffle.options.Verbosity;
import org.jruby.truffle.platform.NativePlatform;
import org.jruby.truffle.platform.NativePlatformFactory;
import org.jruby.truffle.stdlib.CoverageManager;
import org.jruby.truffle.stdlib.readline.ConsoleHolder;
import org.jruby.truffle.tools.InstrumentationServerManager;
import org.jruby.truffle.tools.callgraph.CallGraph;
import org.jruby.truffle.tools.callgraph.SimpleWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.CodeSource;
public class RubyContext extends ExecutionContext {
private final TruffleLanguage.Env env;
private final Options options;
private final String rubyHome;
private String originalInputFile;
private InputStream syntaxCheckInputStream;
private boolean verbose;
private final RopeTable ropeTable = new RopeTable();
private final PrimitiveManager primitiveManager = new PrimitiveManager();
private final SafepointManager safepointManager = new SafepointManager(this);
private final SymbolTable symbolTable;
private final InteropManager interopManager = new InteropManager(this);
private final CodeLoader codeLoader = new CodeLoader(this);
private final FeatureLoader featureLoader = new FeatureLoader(this);
private final TraceManager traceManager;
private final ObjectSpaceManager objectSpaceManager = new ObjectSpaceManager(this);
private final SharedObjects sharedObjects = new SharedObjects(this);
private final AtExitManager atExitManager = new AtExitManager(this);
private final SourceLoader sourceLoader = new SourceLoader(this);
private final CallStackManager callStack = new CallStackManager(this);
private final CoreStrings coreStrings = new CoreStrings(this);
private final FrozenStrings frozenStrings = new FrozenStrings(this);
private final CoreExceptions coreExceptions = new CoreExceptions(this);
private final EncodingManager encodingManager = new EncodingManager(this);
private final CompilerOptions compilerOptions = Truffle.getRuntime().createCompilerOptions();
private final NativePlatform nativePlatform;
private final CoreLibrary coreLibrary;
private final CoreMethods coreMethods;
private final ThreadManager threadManager;
private final LexicalScope rootLexicalScope;
private final InstrumentationServerManager instrumentationServerManager;
private final CallGraph callGraph;
private final PrintStream debugStandardOut;
private final CoverageManager coverageManager;
private final ConsoleHolder consoleHolder;
private final Object classVariableDefinitionLock = new Object();
private String currentDirectory;
public static ThreadLocal contextsBeingCreated = new ThreadLocal<>();
public RubyContext(TruffleLanguage.Env env) {
contextsBeingCreated.set(this);
try {
this.env = env;
final OptionsBuilder optionsBuilder = new OptionsBuilder();
optionsBuilder.set(env.getConfig());
optionsBuilder.set(System.getProperties());
options = optionsBuilder.build();
rubyHome = findRubyHome();
Log.LOGGER.config(() -> String.format("ruby home: %s", rubyHome));
currentDirectory = System.getProperty("user.dir");
verbose = options.VERBOSITY.equals(Verbosity.TRUE);
if (options.CALL_GRAPH) {
callGraph = new CallGraph();
} else {
callGraph = null;
}
// Stuff that needs to be loaded before we load any code
/*
* The Graal option TimeThreshold sets how long a method has to become hot after it has started running, in ms.
* This is designed to not try to compile cold methods that just happen to be called enough times during a
* very long running program. We haven't worked out the best value of this for Ruby yet, and the default value
* produces poor benchmark results. Here we just set it to a very high value, to effectively disable it.
*/
if (compilerOptions.supportsOption("MinTimeThreshold")) {
compilerOptions.setOption("MinTimeThreshold", 100000000);
}
/*
* The Graal option InliningMaxCallerSize sets the maximum size of a method for where we consider to inline
* calls from that method. So it's the caller method we're talking about, not the called method. The default
* value doesn't produce good results for Ruby programs, but we aren't sure why yet. Perhaps it prevents a few
* key methods from the core library from inlining other methods.
*/
if (compilerOptions.supportsOption("MinInliningMaxCallerSize")) {
compilerOptions.setOption("MinInliningMaxCallerSize", 5000);
}
// Load the core library classes
coreLibrary = new CoreLibrary(this);
coreLibrary.initialize();
symbolTable = new SymbolTable(coreLibrary.getSymbolFactory());
// Create objects that need core classes
nativePlatform = NativePlatformFactory.createPlatform(this);
rootLexicalScope = new LexicalScope(null, coreLibrary.getObjectClass());
// The encoding manager relies on POSIX having been initialized, so we can't process it during
// normal core library initialization.
coreLibrary.initializeEncodingManager();
threadManager = new ThreadManager(this);
threadManager.initialize();
// Load the nodes
Main.printTruffleTimeMetric("before-load-nodes");
coreLibrary.addCoreMethods(primitiveManager);
Main.printTruffleTimeMetric("after-load-nodes");
// Capture known builtin methods
final Instrumenter instrumenter = env.lookup(Instrumenter.class);
traceManager = new TraceManager(this, instrumenter);
coreMethods = new CoreMethods(this);
coverageManager = new CoverageManager(this, instrumenter);
// Load the reset of the core library
coreLibrary.loadRubyCore();
// Load other subsystems
final PrintStream configStandardOut = System.out;
debugStandardOut = (configStandardOut == System.out) ? null : configStandardOut;
// The instrumentation server can't be run with AOT because com.sun.net.httpserver.spi.HttpServerProvider uses runtime class loading.
if (!TruffleOptions.AOT && options.INSTRUMENTATION_SERVER_PORT != 0) {
instrumentationServerManager = new InstrumentationServerManager(this, options.INSTRUMENTATION_SERVER_PORT);
instrumentationServerManager.start();
} else {
instrumentationServerManager = null;
}
coreLibrary.initializePostBoot();
consoleHolder = new ConsoleHolder();
// Share once everything is loaded
if (options.SHARED_OBJECTS_ENABLED && options.SHARED_OBJECTS_FORCE) {
sharedObjects.startSharing();
}
} finally {
contextsBeingCreated.remove();
}
}
private String findRubyHome() {
// Use the option if it was set
if (options.HOME != null) {
return new File(options.HOME).getAbsolutePath();
}
// Try to find it automatically from the location of the JAR, but this won't work from the JRuby launcher as it uses the boot classpath
if (!TruffleOptions.AOT) {
final CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
if (codeSource != null && codeSource.getLocation().getProtocol().equals("file")) {
final File jar = new File(codeSource.getLocation().getFile());
if (jar.getParentFile().getName().equals("lib")) {
// Conventional build or distribution
return jar.getParentFile().getParentFile().getAbsolutePath();
} else if (jar.getParentFile().getName().equals("ruby") && new File(jar.getParentFile(), "lib").exists()) {
// GraalVM build or distribution
return jar.getParentFile().getAbsolutePath();
} else if (jar.getParentFile().getName().equals("dists") && jar.getParentFile().getParentFile().getName().equals("mxbuild")) {
// mx build
return new File(jar.getParentFile().getParentFile(), "ruby-zip-extracted").getAbsolutePath();
}
}
}
// Just for now, use jruby.home as the launcher sets that
final String jrubyHome = System.getProperty("jruby.home");
if (jrubyHome != null) {
return new File(jrubyHome).getAbsolutePath();
}
Log.LOGGER.config("home not explicitly set, and couldn't determine it from the source of the Java classfiles or the JRuby launcher");
return null;
}
public Object send(Object object, String methodName, DynamicObject block, Object... arguments) {
CompilerAsserts.neverPartOfCompilation();
assert block == null || RubyGuards.isRubyProc(block);
final InternalMethod method = ModuleOperations.lookupMethod(coreLibrary.getMetaClass(object), methodName);
if (method == null || method.isUndefined()) {
return null;
}
return method.getCallTarget().call(
RubyArguments.pack(null, null, method, DeclarationContext.METHOD, null, object, block, arguments));
}
public void shutdown() {
if (options.ROPE_PRINT_INTERN_STATS) {
System.out.println("Ropes re-used: " + getRopeTable().getRopesReusedCount());
System.out.println("Rope byte arrays re-used: " + getRopeTable().getByteArrayReusedCount());
System.out.println("Rope bytes saved: " + getRopeTable().getRopeBytesSaved());
System.out.println("Total ropes interned: " + getRopeTable().totalRopes());
}
atExitManager.runSystemExitHooks();
if (instrumentationServerManager != null) {
instrumentationServerManager.shutdown();
}
threadManager.shutdown();
if (options.COVERAGE_GLOBAL) {
coverageManager.print(System.out);
}
if (callGraph != null) {
callGraph.resolve();
if (options.CALL_GRAPH_WRITE != null) {
try (PrintStream stream = new PrintStream(options.CALL_GRAPH_WRITE, StandardCharsets.UTF_8.name())) {
new SimpleWriter(callGraph, stream).write();
} catch (FileNotFoundException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
public Options getOptions() {
return options;
}
public TruffleLanguage.Env getEnv() {
return env;
}
public NativePlatform getNativePlatform() {
return nativePlatform;
}
public CoreLibrary getCoreLibrary() {
return coreLibrary;
}
public CoreMethods getCoreMethods() {
return coreMethods;
}
public PrintStream getDebugStandardOut() {
return debugStandardOut;
}
public FeatureLoader getFeatureLoader() {
return featureLoader;
}
public ObjectSpaceManager getObjectSpaceManager() {
return objectSpaceManager;
}
public SharedObjects getSharedObjects() {
return sharedObjects;
}
public ThreadManager getThreadManager() {
return threadManager;
}
public AtExitManager getAtExitManager() {
return atExitManager;
}
public TraceManager getTraceManager() {
return traceManager;
}
public SafepointManager getSafepointManager() {
return safepointManager;
}
public LexicalScope getRootLexicalScope() {
return rootLexicalScope;
}
@Override
public CompilerOptions getCompilerOptions() {
return compilerOptions;
}
public PrimitiveManager getPrimitiveManager() {
return primitiveManager;
}
public CoverageManager getCoverageManager() {
return coverageManager;
}
public static RubyContext getInstance() {
try {
return RubyLanguage.INSTANCE.unprotectedFindContext(RubyLanguage.INSTANCE.unprotectedCreateFindContextNode());
} catch (IllegalStateException e) {
return contextsBeingCreated.get();
}
}
public SourceLoader getSourceLoader() {
return sourceLoader;
}
public RopeTable getRopeTable() {
return ropeTable;
}
public SymbolTable getSymbolTable() {
return symbolTable;
}
public CallGraph getCallGraph() {
return callGraph;
}
public CodeLoader getCodeLoader() {
return codeLoader;
}
public InteropManager getInteropManager() {
return interopManager;
}
public CallStackManager getCallStack() {
return callStack;
}
public CoreStrings getCoreStrings() {
return coreStrings;
}
public FrozenStrings getFrozenStrings() {
return frozenStrings;
}
public Object getClassVariableDefinitionLock() {
return classVariableDefinitionLock;
}
public Instrumenter getInstrumenter() {
return env.lookup(Instrumenter.class);
}
public CoreExceptions getCoreExceptions() {
return coreExceptions;
}
public EncodingManager getEncodingManager() {
return encodingManager;
}
public String getCurrentDirectory() {
return currentDirectory;
}
public void setCurrentDirectory(String currentDirectory) {
this.currentDirectory = currentDirectory;
}
public void setOriginalInputFile(String originalInputFile) {
this.originalInputFile = originalInputFile;
}
public String getOriginalInputFile() {
return originalInputFile;
}
public String getRubyHome() {
return rubyHome;
}
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public void setVerboseNil() {
verbose = false;
}
public boolean warningsEnabled() {
return verbose;
}
public boolean isVerbose() {
return verbose;
}
public InputStream getSyntaxCheckInputStream() {
return syntaxCheckInputStream;
}
public void setSyntaxCheckInputStream(InputStream syntaxCheckInputStream) {
this.syntaxCheckInputStream = syntaxCheckInputStream;
}
public ConsoleHolder getConsoleHolder() {
return consoleHolder;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy