Skip to content

Commit a381e65

Browse files
committed
[GR-61679] Adopt Source options and use them to replace mime type workarounds
PullRequest: truffleruby/4462
2 parents aaff490 + e945a4e commit a381e65

38 files changed

+131
-168
lines changed

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,11 @@
3131
import com.oracle.truffle.api.exception.AbstractTruffleException;
3232
import com.oracle.truffle.api.nodes.EncapsulatingNodeReference;
3333
import com.oracle.truffle.api.nodes.Node;
34-
import com.oracle.truffle.api.source.SourceSection;
3534
import com.oracle.truffle.api.utilities.AssumedValue;
3635
import org.graalvm.collections.Pair;
3736
import org.graalvm.options.OptionDescriptor;
3837
import org.truffleruby.cext.ValueWrapperManager;
3938
import org.truffleruby.collections.SharedIndicesMap.ContextArray;
40-
import org.truffleruby.collections.ConcurrentWeakKeysMap;
4139
import org.truffleruby.core.CoreLibrary;
4240
import org.truffleruby.core.DataObjectFinalizationService;
4341
import org.truffleruby.core.FinalizationService;
@@ -126,7 +124,6 @@ public final class RubyContext {
126124
private final PreInitializationManager preInitializationManager;
127125
private final NativeConfiguration nativeConfiguration;
128126
private final ValueWrapperManager valueWrapperManager;
129-
private final ConcurrentWeakKeysMap<Source, Integer> sourceLineOffsets = new ConcurrentWeakKeysMap<>();
130127
/** (Symbol, refinements) -> Proc for Symbol#to_proc */
131128
public final Map<Pair<RubySymbol, Map<RubyModule, RubyModule[]>>, RootCallTarget> cachedSymbolToProcTargetsWithRefinements = new ConcurrentHashMap<>();
132129
/** Default signal handlers for Ruby, only SIGINT and SIGALRM, see {@code core/main.rb} */
@@ -721,15 +718,6 @@ public ValueWrapperManager getValueWrapperManager() {
721718
return valueWrapperManager;
722719
}
723720

724-
public ConcurrentWeakKeysMap<Source, Integer> getSourceLineOffsets() {
725-
return sourceLineOffsets;
726-
}
727-
728-
@TruffleBoundary
729-
public String fileLine(SourceSection section) {
730-
return language.fileLine(this, section);
731-
}
732-
733721
private static SecureRandom createRandomInstance() {
734722
try {
735723
/* We want to use a non-blocking source because this is what MRI does (via /dev/urandom) and it's been found

src/main/java/org/truffleruby/RubyFileTypeDetector.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,20 @@ public String findMimeType(TruffleFile file) throws IOException {
4242

4343
for (String candidate : KNOWN_RUBY_SUFFIXES) {
4444
if (lowerCaseFileName.endsWith(candidate)) {
45-
return RubyLanguage.getMimeType(false);
45+
return RubyLanguage.MIME_TYPE;
4646
}
4747
}
4848

4949
for (String candidate : KNOWN_RUBY_FILES) {
5050
if (fileName.equals(candidate)) {
51-
return RubyLanguage.getMimeType(false);
51+
return RubyLanguage.MIME_TYPE;
5252
}
5353
}
5454

5555
try (BufferedReader fileContent = file.newBufferedReader(StandardCharsets.ISO_8859_1)) {
5656
final String firstLine = fileContent.readLine();
5757
if (firstLine != null && SHEBANG_REGEXP.matcher(firstLine).matches()) {
58-
return RubyLanguage.getMimeType(false);
58+
return RubyLanguage.MIME_TYPE;
5959
}
6060
} catch (IOException | SecurityException e) {
6161
// Reading random files could cause all sorts of errors

src/main/java/org/truffleruby/RubyLanguage.java

Lines changed: 36 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.oracle.truffle.api.CompilerAsserts;
2525
import com.oracle.truffle.api.CompilerDirectives;
2626
import com.oracle.truffle.api.ContextThreadLocal;
27+
import com.oracle.truffle.api.Option;
2728
import com.oracle.truffle.api.TruffleFile;
2829
import com.oracle.truffle.api.frame.FrameDescriptor;
2930
import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -41,7 +42,9 @@
4142
import com.oracle.truffle.api.strings.InternalByteArray;
4243
import com.oracle.truffle.api.strings.TruffleString;
4344
import org.graalvm.nativeimage.ImageInfo;
45+
import org.graalvm.options.OptionCategory;
4446
import org.graalvm.options.OptionDescriptors;
47+
import org.graalvm.options.OptionKey;
4548
import org.prism.Parser;
4649
import org.truffleruby.annotations.SuppressFBWarnings;
4750
import org.truffleruby.builtins.PrimitiveManager;
@@ -123,7 +126,6 @@
123126
import org.truffleruby.parser.BlockDescriptorInfo;
124127
import org.truffleruby.parser.ParserContext;
125128
import org.truffleruby.parser.ParsingParameters;
126-
import org.truffleruby.parser.RubySource;
127129
import org.truffleruby.parser.TranslatorEnvironment;
128130
import org.truffleruby.shared.Platform;
129131
import org.truffleruby.shared.Metrics;
@@ -158,10 +160,7 @@
158160
id = TruffleRuby.LANGUAGE_ID,
159161
implementationName = TruffleRuby.FORMAL_NAME,
160162
version = TruffleRuby.LANGUAGE_VERSION,
161-
characterMimeTypes = {
162-
RubyLanguage.MIME_TYPE,
163-
RubyLanguage.MIME_TYPE_COVERAGE,
164-
RubyLanguage.MIME_TYPE_MAIN_SCRIPT },
163+
characterMimeTypes = RubyLanguage.MIME_TYPE,
165164
defaultMimeType = RubyLanguage.MIME_TYPE,
166165
dependentLanguages = { "nfi", "llvm", "regex" },
167166
fileTypeDetectors = RubyFileTypeDetector.class)
@@ -178,11 +177,23 @@
178177
})
179178
public final class RubyLanguage extends TruffleLanguage<RubyContext> {
180179

181-
/** Do not access directly, instead use {@link #getMimeType(boolean)} */
182-
static final String MIME_TYPE = "application/x-ruby";
183-
public static final String MIME_TYPE_COVERAGE = "application/x-ruby;coverage=true";
184-
public static final String MIME_TYPE_MAIN_SCRIPT = "application/x-ruby;main-script=true";
185-
public static final String[] MIME_TYPES = { MIME_TYPE, MIME_TYPE_COVERAGE, MIME_TYPE_MAIN_SCRIPT };
180+
@Option.Group(TruffleRuby.LANGUAGE_ID)
181+
public abstract static class RubySourceOptions {
182+
// @formatter:off
183+
@Option(help = "Whether Ruby coverage is enabled for this source", category = OptionCategory.INTERNAL)
184+
public static final OptionKey<Boolean> Coverage = new OptionKey<>(false);
185+
186+
@Option(help = "Mark this source as the main Ruby script ($0)", category = OptionCategory.INTERNAL)
187+
public static final OptionKey<Boolean> MainScript = new OptionKey<>(false);
188+
189+
@Option(help = "Record the line offset when this source was eval'ed", category = OptionCategory.INTERNAL)
190+
public static final OptionKey<Integer> LineOffset = new OptionKey<>(0);
191+
// @formatter:on
192+
}
193+
194+
public static final String MIME_TYPE = "application/x-ruby";
195+
/** To avoid some String[] allocations */
196+
public static final String[] MIME_TYPES = { MIME_TYPE };
186197

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

@@ -366,10 +377,6 @@ public static RubyLanguage get(Node node) {
366377
return REFERENCE.get(node);
367378
}
368379

369-
public static String getMimeType(boolean coverageEnabled) {
370-
return coverageEnabled ? MIME_TYPE_COVERAGE : MIME_TYPE;
371-
}
372-
373380
public RubyLanguage() {
374381
coreMethodAssumptions = new CoreMethodAssumptions(this);
375382
coreStrings = new CoreStrings(this);
@@ -469,7 +476,7 @@ public RubyContext createContext(Env env) {
469476
this.coreLoadPath = buildCoreLoadPath(this.options.CORE_LOAD_PATH);
470477
this.corePath = coreLoadPath + File.separator + "core" + File.separator;
471478
Instrumenter instrumenter = Objects.requireNonNull(env.lookup(Instrumenter.class));
472-
this.coverageManager = new CoverageManager(options, instrumenter);
479+
this.coverageManager = new CoverageManager(this, instrumenter);
473480
if (options.INSTRUMENT_ALL_NODES) {
474481
instrumentAllNodes(instrumenter);
475482
}
@@ -597,7 +604,7 @@ protected RootCallTarget parse(ParsingRequest request) {
597604
final ParsingParameters parsingParameters = parsingRequestParams.get();
598605
if (parsingParameters != null) { // from #require or core library
599606
assert parsingParameters.rubySource.getSource().equals(source);
600-
final ParserContext parserContext = MIME_TYPE_MAIN_SCRIPT.equals(source.getMimeType())
607+
final ParserContext parserContext = request.getOptionValues().get(RubySourceOptions.MainScript)
601608
? ParserContext.TOP_LEVEL_FIRST
602609
: ParserContext.TOP_LEVEL;
603610
final LexicalScope lexicalScope = contextIfSingleContext.map(RubyContext::getRootLexicalScope).orElse(null);
@@ -634,6 +641,11 @@ protected OptionDescriptors getOptionDescriptors() {
634641
return OptionDescriptors.create(Arrays.asList(OptionsCatalog.allDescriptors()));
635642
}
636643

644+
@Override
645+
protected OptionDescriptors getSourceOptionDescriptors() {
646+
return new RubySourceOptionsOptionDescriptors();
647+
}
648+
637649
@Override
638650
protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) {
639651
return true;
@@ -944,18 +956,13 @@ public String getSourcePath(Source source) {
944956
}
945957

946958
@TruffleBoundary
947-
public static String getCorePath(Source source) {
948-
final String path = getPath(source);
949-
String coreLoadPath = OptionsCatalog.CORE_LOAD_PATH_KEY.getDefaultValue();
950-
if (path.startsWith(coreLoadPath)) {
951-
return "<internal:core> " + path.substring(coreLoadPath.length() + 1);
952-
} else {
953-
throw CompilerDirectives.shouldNotReachHere(path + " is not a core path starting with " + coreLoadPath);
954-
}
959+
public int getStartLineAdjusted(SourceSection sourceSection) {
960+
int lineOffset = sourceSection.getSource().getOptions(this).get(RubySourceOptions.LineOffset);
961+
return sourceSection.getStartLine() + lineOffset;
955962
}
956963

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

978-
/** Prefer {@link RubyContext#fileLine(SourceSection)} as it is more concise. */
979985
@TruffleBoundary
980-
String fileLine(RubyContext context, SourceSection section) {
986+
public String fileLine(SourceSection section) {
981987
if (section == null) {
982988
return "no source section";
983989
} else {
984990
final String path = getSourcePath(section.getSource());
985991

986992
if (section.isAvailable()) {
987-
return path + ":" + RubySource.getStartLineAdjusted(context, section);
993+
return path + ":" + getStartLineAdjusted(section);
988994
} else {
989995
return path;
990996
}
991997
}
992998
}
993999

994-
/** Only use when no language/context is available (e.g. Node#toString). Prefer
995-
* {@link RubyContext#fileLine(SourceSection)} as it accounts for coreLoadPath and line offsets. */
996-
@TruffleBoundary
997-
public static String filenameLine(SourceSection section) {
998-
if (section == null) {
999-
return "no source section";
1000-
} else {
1001-
final String path = getPath(section.getSource());
1002-
final String filename = new File(path).getName();
1003-
1004-
if (section.isAvailable()) {
1005-
return filename + ":" + section.getStartLine();
1006-
} else {
1007-
return filename;
1008-
}
1009-
}
1010-
}
1011-
1012-
public Object rubySourceLocation(RubyContext context, SourceSection section,
1000+
public Object rubySourceLocation(SourceSection section,
10131001
TruffleString.FromJavaStringNode fromJavaStringNode,
10141002
Node node) {
10151003
if (!BacktraceFormatter.isAvailable(section)) {
10161004
return nil;
10171005
} else {
10181006
var file = createString(node, fromJavaStringNode, getSourcePath(section.getSource()), Encodings.UTF_8);
1019-
Object[] objects = new Object[]{ file, RubySource.getStartLineAdjusted(context, section) };
1007+
Object[] objects = new Object[]{ file, getStartLineAdjusted(section) };
10201008
return createArray(node, objects);
10211009
}
10221010
}

src/main/java/org/truffleruby/cext/CExtNodes.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@
139139
import com.oracle.truffle.api.nodes.Node;
140140
import com.oracle.truffle.api.nodes.RootNode;
141141
import com.oracle.truffle.api.source.SourceSection;
142-
import org.truffleruby.parser.RubySource;
143142

144143
import static org.truffleruby.language.dispatch.DispatchConfiguration.PRIVATE;
145144
import static org.truffleruby.language.dispatch.DispatchConfiguration.PUBLIC;
@@ -1188,7 +1187,7 @@ public abstract static class SourceLineNode extends CoreMethodArrayArgumentsNode
11881187
@Specialization
11891188
int sourceLine() {
11901189
final SourceSection sourceSection = SourceFileNode.getTopUserSourceSection("rb_sourceline");
1191-
return RubySource.getStartLineAdjusted(getContext(), sourceSection);
1190+
return getLanguage().getStartLineAdjusted(sourceSection);
11921191
}
11931192

11941193
}

src/main/java/org/truffleruby/core/binding/BindingNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ public abstract static class SourceLocationNode extends CoreMethodArrayArguments
467467
Object sourceLocation(RubyBinding binding,
468468
@Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
469469
var sourceSection = binding.sourceSection;
470-
return getLanguage().rubySourceLocation(getContext(), sourceSection, fromJavaStringNode, this);
470+
return getLanguage().rubySourceLocation(sourceSection, fromJavaStringNode, this);
471471
}
472472
}
473473

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ public RubySyntaxError syntaxErrorInvalidRetry(Node currentNode) {
10101010
public RubySyntaxError syntaxError(String message, Node currentNode, SourceSection sourceLocation) {
10111011
String messageWithFileLine;
10121012
if (sourceLocation != null) {
1013-
messageWithFileLine = context.fileLine(sourceLocation) + ": " + message;
1013+
messageWithFileLine = language.fileLine(sourceLocation) + ": " + message;
10141014
} else {
10151015
messageWithFileLine = "(unknown):1: " + message;
10161016
}

src/main/java/org/truffleruby/core/fiber/RubyFiber.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public RubyFiber(
135135
public void initialize(RubyLanguage language, RubyContext context, boolean blocking, RubyProc block,
136136
Node currentNode) {
137137
final SourceSection sourceSection = block.getSharedMethodInfo().getSourceSection();
138-
this.sourceLocation = context.fileLine(sourceSection);
138+
this.sourceLocation = language.fileLine(sourceSection);
139139
this.body = block;
140140
this.initializeNode = currentNode;
141141
this.blocking = blocking;

src/main/java/org/truffleruby/core/method/MethodNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ public abstract static class SourceLocationNode extends CoreMethodArrayArguments
235235
Object sourceLocation(RubyMethod method,
236236
@Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
237237
var sourceSection = method.method.getSharedMethodInfo().getSourceSection();
238-
return getLanguage().rubySourceLocation(getContext(), sourceSection, fromJavaStringNode, this);
238+
return getLanguage().rubySourceLocation(sourceSection, fromJavaStringNode, this);
239239
}
240240
}
241241

src/main/java/org/truffleruby/core/method/UnboundMethodNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public abstract static class SourceLocationNode extends CoreMethodArrayArguments
201201
Object sourceLocation(RubyUnboundMethod unboundMethod,
202202
@Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
203203
var sourceSection = unboundMethod.method.getSharedMethodInfo().getSourceSection();
204-
return getLanguage().rubySourceLocation(getContext(), sourceSection, fromJavaStringNode, this);
204+
return getLanguage().rubySourceLocation(sourceSection, fromJavaStringNode, this);
205205
}
206206
}
207207

src/main/java/org/truffleruby/core/module/ModuleFields.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static void debugModuleChain(RubyModule module) {
8585
/** The language is stored here so that we don't need to pass the language to the many callers of getName() (some of
8686
* which we can't like toString()). It shouldn't be used for anything else than {@link #setName(String)} (instead
8787
* pass the RubyLanguage as an argument). */
88-
private final RubyLanguage language;
88+
private final RubyLanguage languageForSetName;
8989
private final SourceSection sourceSection;
9090

9191
private final PrependMarker start;
@@ -164,7 +164,7 @@ public ModuleFields(
164164
String givenBaseName,
165165
RubyModule rubyModule /* not fully initialized yet, should not access any field of it */) {
166166
super(null);
167-
this.language = language;
167+
this.languageForSetName = language;
168168
this.sourceSection = sourceSection;
169169
this.lexicalParent = lexicalParent;
170170
this.givenBaseName = givenBaseName;
@@ -444,8 +444,8 @@ public RubyConstant setConstant(RubyContext context, Node currentNode, String na
444444
}
445445

446446
@TruffleBoundary
447-
public void setAutoloadConstant(RubyContext context, Node currentNode, String name, Object filename,
448-
String javaFilename) {
447+
public void setAutoloadConstant(RubyLanguage language, RubyContext context, Node currentNode, String name,
448+
Object filename, String javaFilename) {
449449
RubyConstant autoloadConstant = setConstantInternal(context, currentNode, name, filename, true);
450450
if (autoloadConstant == null) {
451451
return;
@@ -454,7 +454,7 @@ public void setAutoloadConstant(RubyContext context, Node currentNode, String na
454454
if (context.getOptions().LOG_AUTOLOAD) {
455455
RubyLanguage.LOGGER.info(() -> String.format(
456456
"%s: setting up autoload %s with %s",
457-
context.fileLine(context.getCallStack().getTopMostUserSourceSection()),
457+
language.fileLine(context.getCallStack().getTopMostUserSourceSection()),
458458
autoloadConstant,
459459
filename));
460460
}
@@ -787,7 +787,8 @@ public void setFullName(String name) {
787787
private void setName(String name) {
788788
this.name = name;
789789
if (hasPartialName()) {
790-
this.rubyStringName = language.getFrozenStringLiteral(TStringUtils.utf8TString(name), Encodings.UTF_8);
790+
this.rubyStringName = languageForSetName.getFrozenStringLiteral(TStringUtils.utf8TString(name),
791+
Encodings.UTF_8);
791792
} else {
792793
this.rubyStringName = null;
793794
}

src/main/java/org/truffleruby/core/module/ModuleNodes.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,8 @@ static Object autoload(RubyModule module, Object nameObject, Object filename,
635635
}
636636

637637
final String javaStringFilename = RubyGuards.getJavaString(filenameAsPath);
638-
module.fields.setAutoloadConstant(getContext(node), node, name, filenameAsPath, javaStringFilename);
638+
module.fields.setAutoloadConstant(getLanguage(node), getContext(node), node, name, filenameAsPath,
639+
javaStringFilename);
639640
return nil;
640641
}
641642
}
@@ -1157,7 +1158,7 @@ private static Object getLocation(Node node, ConstantLookupResult lookupResult,
11571158
if (!BacktraceFormatter.isAvailable(sourceSection)) {
11581159
return createEmptyArray(node);
11591160
} else {
1160-
return getLanguage(node).rubySourceLocation(getContext(node), sourceSection, fromJavaStringNode, node);
1161+
return getLanguage(node).rubySourceLocation(sourceSection, fromJavaStringNode, node);
11611162
}
11621163
}
11631164

src/main/java/org/truffleruby/core/proc/ProcNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ Object sourceLocation(RubyProc proc) {
216216
if (!sourceSection.isAvailable() || RubyLanguage.getPath(source).endsWith("/lib/truffle/truffle/cext.rb")) {
217217
return nil;
218218
} else {
219-
return getLanguage().rubySourceLocation(getContext(), sourceSection, fromJavaStringNode, this);
219+
return getLanguage().rubySourceLocation(sourceSection, fromJavaStringNode, this);
220220
}
221221
}
222222

0 commit comments

Comments
 (0)