Skip to content

Commit

Permalink
[GR-61679] Adopt Source options and use them to replace mime type wor…
Browse files Browse the repository at this point in the history
…karounds

PullRequest: truffleruby/4462
  • Loading branch information
eregon committed Feb 19, 2025
2 parents aaff490 + e945a4e commit a381e65
Show file tree
Hide file tree
Showing 38 changed files with 131 additions and 168 deletions.
12 changes: 0 additions & 12 deletions src/main/java/org/truffleruby/RubyContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.nodes.EncapsulatingNodeReference;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.AssumedValue;
import org.graalvm.collections.Pair;
import org.graalvm.options.OptionDescriptor;
import org.truffleruby.cext.ValueWrapperManager;
import org.truffleruby.collections.SharedIndicesMap.ContextArray;
import org.truffleruby.collections.ConcurrentWeakKeysMap;
import org.truffleruby.core.CoreLibrary;
import org.truffleruby.core.DataObjectFinalizationService;
import org.truffleruby.core.FinalizationService;
Expand Down Expand Up @@ -126,7 +124,6 @@ public final class RubyContext {
private final PreInitializationManager preInitializationManager;
private final NativeConfiguration nativeConfiguration;
private final ValueWrapperManager valueWrapperManager;
private final ConcurrentWeakKeysMap<Source, Integer> sourceLineOffsets = new ConcurrentWeakKeysMap<>();
/** (Symbol, refinements) -> Proc for Symbol#to_proc */
public final Map<Pair<RubySymbol, Map<RubyModule, RubyModule[]>>, RootCallTarget> cachedSymbolToProcTargetsWithRefinements = new ConcurrentHashMap<>();
/** Default signal handlers for Ruby, only SIGINT and SIGALRM, see {@code core/main.rb} */
Expand Down Expand Up @@ -721,15 +718,6 @@ public ValueWrapperManager getValueWrapperManager() {
return valueWrapperManager;
}

public ConcurrentWeakKeysMap<Source, Integer> getSourceLineOffsets() {
return sourceLineOffsets;
}

@TruffleBoundary
public String fileLine(SourceSection section) {
return language.fileLine(this, section);
}

private static SecureRandom createRandomInstance() {
try {
/* We want to use a non-blocking source because this is what MRI does (via /dev/urandom) and it's been found
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/truffleruby/RubyFileTypeDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,20 @@ public String findMimeType(TruffleFile file) throws IOException {

for (String candidate : KNOWN_RUBY_SUFFIXES) {
if (lowerCaseFileName.endsWith(candidate)) {
return RubyLanguage.getMimeType(false);
return RubyLanguage.MIME_TYPE;
}
}

for (String candidate : KNOWN_RUBY_FILES) {
if (fileName.equals(candidate)) {
return RubyLanguage.getMimeType(false);
return RubyLanguage.MIME_TYPE;
}
}

try (BufferedReader fileContent = file.newBufferedReader(StandardCharsets.ISO_8859_1)) {
final String firstLine = fileContent.readLine();
if (firstLine != null && SHEBANG_REGEXP.matcher(firstLine).matches()) {
return RubyLanguage.getMimeType(false);
return RubyLanguage.MIME_TYPE;
}
} catch (IOException | SecurityException e) {
// Reading random files could cause all sorts of errors
Expand Down
84 changes: 36 additions & 48 deletions src/main/java/org/truffleruby/RubyLanguage.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.ContextThreadLocal;
import com.oracle.truffle.api.Option;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.MaterializedFrame;
Expand All @@ -41,7 +42,9 @@
import com.oracle.truffle.api.strings.InternalByteArray;
import com.oracle.truffle.api.strings.TruffleString;
import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.options.OptionCategory;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.options.OptionKey;
import org.prism.Parser;
import org.truffleruby.annotations.SuppressFBWarnings;
import org.truffleruby.builtins.PrimitiveManager;
Expand Down Expand Up @@ -123,7 +126,6 @@
import org.truffleruby.parser.BlockDescriptorInfo;
import org.truffleruby.parser.ParserContext;
import org.truffleruby.parser.ParsingParameters;
import org.truffleruby.parser.RubySource;
import org.truffleruby.parser.TranslatorEnvironment;
import org.truffleruby.shared.Platform;
import org.truffleruby.shared.Metrics;
Expand Down Expand Up @@ -158,10 +160,7 @@
id = TruffleRuby.LANGUAGE_ID,
implementationName = TruffleRuby.FORMAL_NAME,
version = TruffleRuby.LANGUAGE_VERSION,
characterMimeTypes = {
RubyLanguage.MIME_TYPE,
RubyLanguage.MIME_TYPE_COVERAGE,
RubyLanguage.MIME_TYPE_MAIN_SCRIPT },
characterMimeTypes = RubyLanguage.MIME_TYPE,
defaultMimeType = RubyLanguage.MIME_TYPE,
dependentLanguages = { "nfi", "llvm", "regex" },
fileTypeDetectors = RubyFileTypeDetector.class)
Expand All @@ -178,11 +177,23 @@
})
public final class RubyLanguage extends TruffleLanguage<RubyContext> {

/** Do not access directly, instead use {@link #getMimeType(boolean)} */
static final String MIME_TYPE = "application/x-ruby";
public static final String MIME_TYPE_COVERAGE = "application/x-ruby;coverage=true";
public static final String MIME_TYPE_MAIN_SCRIPT = "application/x-ruby;main-script=true";
public static final String[] MIME_TYPES = { MIME_TYPE, MIME_TYPE_COVERAGE, MIME_TYPE_MAIN_SCRIPT };
@Option.Group(TruffleRuby.LANGUAGE_ID)
public abstract static class RubySourceOptions {
// @formatter:off
@Option(help = "Whether Ruby coverage is enabled for this source", category = OptionCategory.INTERNAL)
public static final OptionKey<Boolean> Coverage = new OptionKey<>(false);

@Option(help = "Mark this source as the main Ruby script ($0)", category = OptionCategory.INTERNAL)
public static final OptionKey<Boolean> MainScript = new OptionKey<>(false);

@Option(help = "Record the line offset when this source was eval'ed", category = OptionCategory.INTERNAL)
public static final OptionKey<Integer> LineOffset = new OptionKey<>(0);
// @formatter:on
}

public static final String MIME_TYPE = "application/x-ruby";
/** To avoid some String[] allocations */
public static final String[] MIME_TYPES = { MIME_TYPE };

public static final String LLVM_BITCODE_MIME_TYPE = "application/x-llvm-ir-bitcode";

Expand Down Expand Up @@ -366,10 +377,6 @@ public static RubyLanguage get(Node node) {
return REFERENCE.get(node);
}

public static String getMimeType(boolean coverageEnabled) {
return coverageEnabled ? MIME_TYPE_COVERAGE : MIME_TYPE;
}

public RubyLanguage() {
coreMethodAssumptions = new CoreMethodAssumptions(this);
coreStrings = new CoreStrings(this);
Expand Down Expand Up @@ -469,7 +476,7 @@ public RubyContext createContext(Env env) {
this.coreLoadPath = buildCoreLoadPath(this.options.CORE_LOAD_PATH);
this.corePath = coreLoadPath + File.separator + "core" + File.separator;
Instrumenter instrumenter = Objects.requireNonNull(env.lookup(Instrumenter.class));
this.coverageManager = new CoverageManager(options, instrumenter);
this.coverageManager = new CoverageManager(this, instrumenter);
if (options.INSTRUMENT_ALL_NODES) {
instrumentAllNodes(instrumenter);
}
Expand Down Expand Up @@ -597,7 +604,7 @@ protected RootCallTarget parse(ParsingRequest request) {
final ParsingParameters parsingParameters = parsingRequestParams.get();
if (parsingParameters != null) { // from #require or core library
assert parsingParameters.rubySource.getSource().equals(source);
final ParserContext parserContext = MIME_TYPE_MAIN_SCRIPT.equals(source.getMimeType())
final ParserContext parserContext = request.getOptionValues().get(RubySourceOptions.MainScript)
? ParserContext.TOP_LEVEL_FIRST
: ParserContext.TOP_LEVEL;
final LexicalScope lexicalScope = contextIfSingleContext.map(RubyContext::getRootLexicalScope).orElse(null);
Expand Down Expand Up @@ -634,6 +641,11 @@ protected OptionDescriptors getOptionDescriptors() {
return OptionDescriptors.create(Arrays.asList(OptionsCatalog.allDescriptors()));
}

@Override
protected OptionDescriptors getSourceOptionDescriptors() {
return new RubySourceOptionsOptionDescriptors();
}

@Override
protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) {
return true;
Expand Down Expand Up @@ -944,18 +956,13 @@ public String getSourcePath(Source source) {
}

@TruffleBoundary
public static String getCorePath(Source source) {
final String path = getPath(source);
String coreLoadPath = OptionsCatalog.CORE_LOAD_PATH_KEY.getDefaultValue();
if (path.startsWith(coreLoadPath)) {
return "<internal:core> " + path.substring(coreLoadPath.length() + 1);
} else {
throw CompilerDirectives.shouldNotReachHere(path + " is not a core path starting with " + coreLoadPath);
}
public int getStartLineAdjusted(SourceSection sourceSection) {
int lineOffset = sourceSection.getSource().getOptions(this).get(RubySourceOptions.LineOffset);
return sourceSection.getStartLine() + lineOffset;
}

/** Only use when no language/context is available (e.g. Node#toString). Prefer
* {@link RubyContext#fileLine(SourceSection)} as it accounts for coreLoadPath and line offsets. */
* {@link RubyLanguage#fileLine(SourceSection)} as it accounts for coreLoadPath and line offsets. */
@TruffleBoundary
public static String fileLineRange(SourceSection section) {
if (section == null) {
Expand All @@ -975,48 +982,29 @@ public static String fileLineRange(SourceSection section) {
}
}

/** Prefer {@link RubyContext#fileLine(SourceSection)} as it is more concise. */
@TruffleBoundary
String fileLine(RubyContext context, SourceSection section) {
public String fileLine(SourceSection section) {
if (section == null) {
return "no source section";
} else {
final String path = getSourcePath(section.getSource());

if (section.isAvailable()) {
return path + ":" + RubySource.getStartLineAdjusted(context, section);
return path + ":" + getStartLineAdjusted(section);
} else {
return path;
}
}
}

/** Only use when no language/context is available (e.g. Node#toString). Prefer
* {@link RubyContext#fileLine(SourceSection)} as it accounts for coreLoadPath and line offsets. */
@TruffleBoundary
public static String filenameLine(SourceSection section) {
if (section == null) {
return "no source section";
} else {
final String path = getPath(section.getSource());
final String filename = new File(path).getName();

if (section.isAvailable()) {
return filename + ":" + section.getStartLine();
} else {
return filename;
}
}
}

public Object rubySourceLocation(RubyContext context, SourceSection section,
public Object rubySourceLocation(SourceSection section,
TruffleString.FromJavaStringNode fromJavaStringNode,
Node node) {
if (!BacktraceFormatter.isAvailable(section)) {
return nil;
} else {
var file = createString(node, fromJavaStringNode, getSourcePath(section.getSource()), Encodings.UTF_8);
Object[] objects = new Object[]{ file, RubySource.getStartLineAdjusted(context, section) };
Object[] objects = new Object[]{ file, getStartLineAdjusted(section) };
return createArray(node, objects);
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/org/truffleruby/cext/CExtNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;
import org.truffleruby.parser.RubySource;

import static org.truffleruby.language.dispatch.DispatchConfiguration.PRIVATE;
import static org.truffleruby.language.dispatch.DispatchConfiguration.PUBLIC;
Expand Down Expand Up @@ -1188,7 +1187,7 @@ public abstract static class SourceLineNode extends CoreMethodArrayArgumentsNode
@Specialization
int sourceLine() {
final SourceSection sourceSection = SourceFileNode.getTopUserSourceSection("rb_sourceline");
return RubySource.getStartLineAdjusted(getContext(), sourceSection);
return getLanguage().getStartLineAdjusted(sourceSection);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ public abstract static class SourceLocationNode extends CoreMethodArrayArguments
Object sourceLocation(RubyBinding binding,
@Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
var sourceSection = binding.sourceSection;
return getLanguage().rubySourceLocation(getContext(), sourceSection, fromJavaStringNode, this);
return getLanguage().rubySourceLocation(sourceSection, fromJavaStringNode, this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ public RubySyntaxError syntaxErrorInvalidRetry(Node currentNode) {
public RubySyntaxError syntaxError(String message, Node currentNode, SourceSection sourceLocation) {
String messageWithFileLine;
if (sourceLocation != null) {
messageWithFileLine = context.fileLine(sourceLocation) + ": " + message;
messageWithFileLine = language.fileLine(sourceLocation) + ": " + message;
} else {
messageWithFileLine = "(unknown):1: " + message;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/truffleruby/core/fiber/RubyFiber.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public RubyFiber(
public void initialize(RubyLanguage language, RubyContext context, boolean blocking, RubyProc block,
Node currentNode) {
final SourceSection sourceSection = block.getSharedMethodInfo().getSourceSection();
this.sourceLocation = context.fileLine(sourceSection);
this.sourceLocation = language.fileLine(sourceSection);
this.body = block;
this.initializeNode = currentNode;
this.blocking = blocking;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/truffleruby/core/method/MethodNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public abstract static class SourceLocationNode extends CoreMethodArrayArguments
Object sourceLocation(RubyMethod method,
@Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
var sourceSection = method.method.getSharedMethodInfo().getSourceSection();
return getLanguage().rubySourceLocation(getContext(), sourceSection, fromJavaStringNode, this);
return getLanguage().rubySourceLocation(sourceSection, fromJavaStringNode, this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public abstract static class SourceLocationNode extends CoreMethodArrayArguments
Object sourceLocation(RubyUnboundMethod unboundMethod,
@Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
var sourceSection = unboundMethod.method.getSharedMethodInfo().getSourceSection();
return getLanguage().rubySourceLocation(getContext(), sourceSection, fromJavaStringNode, this);
return getLanguage().rubySourceLocation(sourceSection, fromJavaStringNode, this);
}
}

Expand Down
13 changes: 7 additions & 6 deletions src/main/java/org/truffleruby/core/module/ModuleFields.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public static void debugModuleChain(RubyModule module) {
/** The language is stored here so that we don't need to pass the language to the many callers of getName() (some of
* which we can't like toString()). It shouldn't be used for anything else than {@link #setName(String)} (instead
* pass the RubyLanguage as an argument). */
private final RubyLanguage language;
private final RubyLanguage languageForSetName;
private final SourceSection sourceSection;

private final PrependMarker start;
Expand Down Expand Up @@ -164,7 +164,7 @@ public ModuleFields(
String givenBaseName,
RubyModule rubyModule /* not fully initialized yet, should not access any field of it */) {
super(null);
this.language = language;
this.languageForSetName = language;
this.sourceSection = sourceSection;
this.lexicalParent = lexicalParent;
this.givenBaseName = givenBaseName;
Expand Down Expand Up @@ -444,8 +444,8 @@ public RubyConstant setConstant(RubyContext context, Node currentNode, String na
}

@TruffleBoundary
public void setAutoloadConstant(RubyContext context, Node currentNode, String name, Object filename,
String javaFilename) {
public void setAutoloadConstant(RubyLanguage language, RubyContext context, Node currentNode, String name,
Object filename, String javaFilename) {
RubyConstant autoloadConstant = setConstantInternal(context, currentNode, name, filename, true);
if (autoloadConstant == null) {
return;
Expand All @@ -454,7 +454,7 @@ public void setAutoloadConstant(RubyContext context, Node currentNode, String na
if (context.getOptions().LOG_AUTOLOAD) {
RubyLanguage.LOGGER.info(() -> String.format(
"%s: setting up autoload %s with %s",
context.fileLine(context.getCallStack().getTopMostUserSourceSection()),
language.fileLine(context.getCallStack().getTopMostUserSourceSection()),
autoloadConstant,
filename));
}
Expand Down Expand Up @@ -787,7 +787,8 @@ public void setFullName(String name) {
private void setName(String name) {
this.name = name;
if (hasPartialName()) {
this.rubyStringName = language.getFrozenStringLiteral(TStringUtils.utf8TString(name), Encodings.UTF_8);
this.rubyStringName = languageForSetName.getFrozenStringLiteral(TStringUtils.utf8TString(name),
Encodings.UTF_8);
} else {
this.rubyStringName = null;
}
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/org/truffleruby/core/module/ModuleNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,8 @@ static Object autoload(RubyModule module, Object nameObject, Object filename,
}

final String javaStringFilename = RubyGuards.getJavaString(filenameAsPath);
module.fields.setAutoloadConstant(getContext(node), node, name, filenameAsPath, javaStringFilename);
module.fields.setAutoloadConstant(getLanguage(node), getContext(node), node, name, filenameAsPath,
javaStringFilename);
return nil;
}
}
Expand Down Expand Up @@ -1157,7 +1158,7 @@ private static Object getLocation(Node node, ConstantLookupResult lookupResult,
if (!BacktraceFormatter.isAvailable(sourceSection)) {
return createEmptyArray(node);
} else {
return getLanguage(node).rubySourceLocation(getContext(node), sourceSection, fromJavaStringNode, node);
return getLanguage(node).rubySourceLocation(sourceSection, fromJavaStringNode, node);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/truffleruby/core/proc/ProcNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ Object sourceLocation(RubyProc proc) {
if (!sourceSection.isAvailable() || RubyLanguage.getPath(source).endsWith("/lib/truffle/truffle/cext.rb")) {
return nil;
} else {
return getLanguage().rubySourceLocation(getContext(), sourceSection, fromJavaStringNode, this);
return getLanguage().rubySourceLocation(sourceSection, fromJavaStringNode, this);
}
}

Expand Down
Loading

0 comments on commit a381e65

Please sign in to comment.