Skip to content

Commit 316217d

Browse files
committed
[GR-16484] Build the Source with a TruffleFile for better instrumentation.
PullRequest: truffleruby/897
2 parents f504e97 + 2bd5fec commit 316217d

File tree

13 files changed

+112
-184
lines changed

13 files changed

+112
-184
lines changed

ci.jsonnet

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,8 @@ local part_definitions = {
348348

349349
run: {
350350
test_unit_tck_specs: {
351-
run+: [
352-
["mx", "unittest", "org.truffleruby"],
353-
["mx", "tck"],
354-
] + jt(["test", "specs"] + self["$.run.specs"].test_spec_options),
351+
run+: jt(["test", "unit"]) + jt(["test", "tck"]) +
352+
jt(["test", "specs"] + self["$.run.specs"].test_spec_options),
355353
# + jt(["test", "specs", ":next"]) disabled as it's currently empty and MSpec doesn't support empty sets of files
356354
},
357355

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ public String fileLine(SourceSection section) {
850850
}
851851
}
852852

853-
public void setMainSources(Source mainSource, String mainSourceAbsolutePath) {
853+
public void setMainSource(Source mainSource, String mainSourceAbsolutePath) {
854854
this.mainSource = mainSource;
855855
this.mainSourceAbsolutePath = mainSourceAbsolutePath;
856856
}

src/main/java/org/truffleruby/core/CoreLibrary.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ public RubySource loadCoreFile(String feature) throws IOException {
837837
}
838838
} else {
839839
final FileLoader fileLoader = new FileLoader(context);
840-
return fileLoader.loadFile(feature);
840+
return fileLoader.loadFile(context.getEnv(), feature);
841841
}
842842
}
843843

src/main/java/org/truffleruby/core/kernel/TruffleKernelNodes.java

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,8 @@
4040
import com.oracle.truffle.api.dsl.CreateCast;
4141
import com.oracle.truffle.api.dsl.NodeChild;
4242
import com.oracle.truffle.api.dsl.Specialization;
43-
import com.oracle.truffle.api.frame.VirtualFrame;
4443
import com.oracle.truffle.api.nodes.IndirectCallNode;
4544
import com.oracle.truffle.api.object.DynamicObject;
46-
import com.oracle.truffle.api.profiles.BranchProfile;
4745

4846
@CoreClass("Truffle::KernelOperations")
4947
public abstract class TruffleKernelNodes {
@@ -69,34 +67,30 @@ public RubyNode coerceToBoolean(RubyNode inherit) {
6967
return BooleanCastWithDefaultNodeGen.create(false, inherit);
7068
}
7169

70+
@TruffleBoundary
7271
@Specialization(guards = "isRubyString(file)")
73-
public boolean load(VirtualFrame frame, DynamicObject file, boolean wrap,
74-
@Cached("create()") IndirectCallNode callNode,
75-
@Cached("create()") BranchProfile errorProfile) {
72+
public boolean load(DynamicObject file, boolean wrap,
73+
@Cached("create()") IndirectCallNode callNode) {
7674
if (wrap) {
7775
throw new UnsupportedOperationException();
7876
}
7977

8078
final String feature = StringOperations.getString(file);
79+
final RubySource source;
8180
try {
82-
final RubySource source = loadFile(feature);
83-
final RubyRootNode rootNode = getContext().getCodeLoader().parse(source, ParserContext.TOP_LEVEL, null, true, this);
84-
final CodeLoader.DeferredCall deferredCall = getContext().getCodeLoader().prepareExecute(
85-
ParserContext.TOP_LEVEL, DeclarationContext.topLevel(getContext()), rootNode, null,
86-
getContext().getCoreLibrary().getMainObject());
87-
deferredCall.call(callNode);
81+
final FileLoader fileLoader = new FileLoader(getContext());
82+
source = fileLoader.loadFile(getContext().getEnv(), feature);
8883
} catch (IOException e) {
89-
errorProfile.enter();
9084
throw new RaiseException(getContext(), coreExceptions().loadErrorCannotLoad(feature, this));
9185
}
9286

93-
return true;
94-
}
87+
final RubyRootNode rootNode = getContext().getCodeLoader().parse(source, ParserContext.TOP_LEVEL, null, true, this);
88+
final CodeLoader.DeferredCall deferredCall = getContext().getCodeLoader().prepareExecute(
89+
ParserContext.TOP_LEVEL, DeclarationContext.topLevel(getContext()), rootNode, null,
90+
getContext().getCoreLibrary().getMainObject());
91+
deferredCall.call(callNode);
9592

96-
@TruffleBoundary
97-
private RubySource loadFile(String feature) throws IOException {
98-
final FileLoader fileLoader = new FileLoader(getContext());
99-
return fileLoader.loadFile(feature);
93+
return true;
10094
}
10195

10296
}

src/main/java/org/truffleruby/language/TruffleBootNodes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ private RubySource loadMainSourceSettingDollarZero(StringNodes.MakeStringNode ma
176176
switch (kind) {
177177
case "FILE": {
178178
final MainLoader mainLoader = new MainLoader(getContext());
179-
source = mainLoader.loadFromFile(this, toExecute);
179+
source = mainLoader.loadFromFile(getContext().getEnv(), this, toExecute);
180180
dollarZeroValue = makeStringNode.executeMake(toExecute, UTF8Encoding.INSTANCE, CodeRange.CR_UNKNOWN);
181181
} break;
182182

src/main/java/org/truffleruby/language/loader/FileLoader.java

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
*/
1010
package org.truffleruby.language.loader;
1111

12+
import com.oracle.truffle.api.TruffleFile;
13+
import com.oracle.truffle.api.TruffleLanguage.Env;
1214
import com.oracle.truffle.api.source.Source;
1315
import org.jcodings.specific.UTF8Encoding;
1416
import org.truffleruby.RubyContext;
@@ -22,9 +24,7 @@
2224

2325
import java.io.File;
2426
import java.io.IOException;
25-
import java.nio.file.Files;
2627
import java.nio.file.Paths;
27-
import java.util.Locale;
2828

2929
/*
3030
* Loads normal Ruby source files from the file system.
@@ -37,24 +37,23 @@ public FileLoader(RubyContext context) {
3737
this.context = context;
3838
}
3939

40-
public void ensureReadable(String path) {
41-
final File file = new File(path);
42-
40+
public void ensureReadable(TruffleFile file) {
4341
if (!file.exists()) {
44-
throw new RaiseException(context, context.getCoreExceptions().loadError("No such file or directory -- " + path, path, null));
42+
throw new RaiseException(context, context.getCoreExceptions().loadError("No such file or directory -- " + file, file.toString(), null));
4543
}
4644

47-
if (!file.canRead()) {
48-
throw new RaiseException(context, context.getCoreExceptions().loadError("Permission denied -- " + path, path, null));
45+
if (!file.isReadable()) {
46+
throw new RaiseException(context, context.getCoreExceptions().loadError("Permission denied -- " + file, file.toString(), null));
4947
}
5048
}
5149

52-
public RubySource loadFile(String path) throws IOException {
50+
public RubySource loadFile(Env env, String path) throws IOException {
5351
if (context.getOptions().LOG_LOAD) {
5452
RubyLanguage.LOGGER.info("loading " + path);
5553
}
5654

57-
ensureReadable(path);
55+
final TruffleFile file = env.getTruffleFile(path);
56+
ensureReadable(file);
5857

5958
final String name;
6059

@@ -65,33 +64,36 @@ public RubySource loadFile(String path) throws IOException {
6564
}
6665

6766
/*
68-
* We must read the file bytes ourselves - otherwise Truffle will read them, assume they're UTF-8, and we will
69-
* not be able to re-interpret the encoding later without the risk of the values being corrupted by being
70-
* passed through UTF-8.
67+
* We read the file's bytes ourselves because the lexer works on bytes and Truffle only gives us a CharSequence.
68+
* We could convert the CharSequence back to bytes, but that's more expensive than just reading the bytes once
69+
* and pass them down to the lexer and to the Source.
7170
*/
7271

73-
final byte[] sourceBytes = Files.readAllBytes(Paths.get(path));
72+
final byte[] sourceBytes = file.readAllBytes();
7473
final Rope sourceRope = RopeOperations.create(sourceBytes, UTF8Encoding.INSTANCE, CodeRange.CR_UNKNOWN);
75-
final String language;
76-
final String mimeType;
7774

78-
if (path.toLowerCase(Locale.ENGLISH).endsWith(RubyLanguage.CEXT_EXTENSION)) {
79-
language = TruffleRuby.LLVM_ID;
80-
mimeType = RubyLanguage.CEXT_MIME_TYPE;
81-
} else {
82-
// We need to assume all other files are Ruby, so the file type detection isn't enough
83-
language = TruffleRuby.LANGUAGE_ID;
84-
mimeType = TruffleRuby.MIME_TYPE;
85-
}
75+
final Source source = buildSource(file, name, sourceRope, isInternal(path));
8676

87-
// We set an explicit MIME type because LLVM does not have a default one
77+
return new RubySource(source, sourceRope);
78+
}
8879

89-
final Source source = Source.newBuilder(language, sourceRope.toString(), name)
90-
.mimeType(mimeType)
91-
.internal(isInternal(path))
92-
.build();
80+
Source buildSource(TruffleFile file, String name, Rope sourceRope, boolean internal) {
81+
/*
82+
* I'm not sure why we need to explicitly set a MIME type here - we say it's Ruby and this is the only and
83+
* default MIME type that Ruby supports.
84+
*
85+
* But if you remove setting the MIME type you get the following failures:
86+
*
87+
* - test/truffle/compiler/stf-optimises.sh (I think the value is different, not the compilation that fails)
88+
* - test/truffle/integration/tracing.sh (again, probably the values, and I'm not sure we were correct before, it's just changed)
89+
*/
9390

94-
return new RubySource(source, sourceRope);
91+
return Source.newBuilder(TruffleRuby.LANGUAGE_ID, file)
92+
.mimeType(TruffleRuby.MIME_TYPE)
93+
.name(name)
94+
.content(RopeOperations.decodeOrEscapeBinaryRope(sourceRope))
95+
.internal(internal)
96+
.build();
9597
}
9698

9799
private boolean isInternal(String path) {

src/main/java/org/truffleruby/language/loader/MainLoader.java

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
*/
1010
package org.truffleruby.language.loader;
1111

12+
import com.oracle.truffle.api.TruffleFile;
13+
import com.oracle.truffle.api.TruffleLanguage.Env;
1214
import com.oracle.truffle.api.source.Source;
1315
import org.jcodings.specific.UTF8Encoding;
1416
import org.truffleruby.RubyContext;
@@ -22,8 +24,6 @@
2224
import java.io.ByteArrayOutputStream;
2325
import java.io.File;
2426
import java.io.IOException;
25-
import java.nio.file.Files;
26-
import java.nio.file.Paths;
2727

2828
/*
2929
* Loads the main script, whether it comes from an argument, standard in, or a file.
@@ -43,16 +43,21 @@ public RubySource loadFromCommandLineArgument(String code) {
4343

4444
public RubySource loadFromStandardIn(RubyNode currentNode, String path) throws IOException {
4545
byte[] sourceBytes = readAllOfStandardIn();
46+
final Rope sourceRope = transformScript(currentNode, path, sourceBytes);
4647

48+
final Source source = Source.newBuilder(TruffleRuby.LANGUAGE_ID,
49+
RopeOperations.decodeOrEscapeBinaryRope(sourceRope), path).build();
50+
return new RubySource(source, sourceRope);
51+
}
52+
53+
private Rope transformScript(RubyNode currentNode, String path, byte[] sourceBytes) throws IOException {
4754
final EmbeddedScript embeddedScript = new EmbeddedScript(context);
4855

4956
if (embeddedScript.shouldTransform(sourceBytes)) {
5057
sourceBytes = embeddedScript.transformForExecution(currentNode, sourceBytes, path);
5158
}
5259

53-
final Rope sourceRope = RopeOperations.create(sourceBytes, UTF8Encoding.INSTANCE, CodeRange.CR_UNKNOWN);
54-
final Source source = Source.newBuilder(TruffleRuby.LANGUAGE_ID, sourceRope.toString(), path).build();
55-
return new RubySource(source, sourceRope);
60+
return RopeOperations.create(sourceBytes, UTF8Encoding.INSTANCE, CodeRange.CR_UNKNOWN);
5661
}
5762

5863
private byte[] readAllOfStandardIn() throws IOException {
@@ -73,39 +78,23 @@ private byte[] readAllOfStandardIn() throws IOException {
7378
return byteStream.toByteArray();
7479
}
7580

76-
public RubySource loadFromFile(RubyNode currentNode, String path) throws IOException {
81+
public RubySource loadFromFile(Env env, RubyNode currentNode, String path) throws IOException {
7782
final FileLoader fileLoader = new FileLoader(context);
78-
fileLoader.ensureReadable(path);
79-
80-
/*
81-
* We must read the file bytes ourselves - otherwise Truffle will read them, assume they're UTF-8, and we will
82-
* not be able to re-interpret the encoding later without the risk of the values being corrupted by being
83-
* passed through UTF-8.
84-
*/
85-
86-
byte[] sourceBytes = Files.readAllBytes(Paths.get(path));
87-
88-
final EmbeddedScript embeddedScript = new EmbeddedScript(context);
89-
90-
if (embeddedScript.shouldTransform(sourceBytes)) {
91-
sourceBytes = embeddedScript.transformForExecution(currentNode, sourceBytes, path);
92-
}
9383

94-
final Rope sourceRope = RopeOperations.create(sourceBytes, UTF8Encoding.INSTANCE, CodeRange.CR_UNKNOWN);
84+
final TruffleFile file = env.getTruffleFile(path);
85+
fileLoader.ensureReadable(file);
9586

9687
/*
97-
* I'm not sure why we need to explicitly set a MIME type here - we say it's Ruby and this is the only and
98-
* default MIME type that Ruby supports.
99-
*
100-
* But if you remove setting the MIME type you get the following failures:
101-
*
102-
* - test/truffle/compiler/stf-optimises.sh (I think the value is different, not the compilation that fails)
103-
* - test/truffle/integration/tracing.sh (again, probably the values, and I'm not sure we were correct before, it's just changed)
88+
* We read the file's bytes ourselves because the lexer works on bytes and Truffle only gives us a CharSequence.
89+
* We could convert the CharSequence back to bytes, but that's more expensive than just reading the bytes once
90+
* and pass them down to the lexer and to the Source.
10491
*/
10592

106-
final Source source = Source.newBuilder(TruffleRuby.LANGUAGE_ID, sourceRope.toString(), path).mimeType(TruffleRuby.MIME_TYPE).build();
93+
byte[] sourceBytes = file.readAllBytes();
94+
final Rope sourceRope = transformScript(currentNode, path, sourceBytes);
10795

108-
context.setMainSources(source, new File(path).getAbsolutePath());
96+
final Source source = fileLoader.buildSource(file, path, sourceRope, false);
97+
context.setMainSource(source, new File(path).getAbsolutePath());
10998

11099
return new RubySource(source, sourceRope);
111100
}

src/main/java/org/truffleruby/language/loader/RequireNode.java

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import com.oracle.truffle.api.object.DynamicObject;
2525
import com.oracle.truffle.api.profiles.BranchProfile;
2626
import com.oracle.truffle.api.profiles.ConditionProfile;
27-
import com.oracle.truffle.api.source.Source;
2827
import com.oracle.truffle.api.source.SourceSection;
2928
import org.jcodings.specific.UTF8Encoding;
3029
import org.truffleruby.RubyLanguage;
@@ -45,13 +44,13 @@
4544
import org.truffleruby.parser.ParserContext;
4645
import org.truffleruby.parser.RubySource;
4746
import org.truffleruby.shared.Metrics;
48-
import org.truffleruby.shared.TruffleRuby;
4947

5048
import java.io.File;
5149
import java.io.IOException;
5250
import java.nio.file.Path;
5351
import java.nio.file.Paths;
5452
import java.util.List;
53+
import java.util.Locale;
5554
import java.util.concurrent.ConcurrentMap;
5655
import java.util.concurrent.locks.ReentrantLock;
5756

@@ -202,17 +201,18 @@ private boolean doRequire(String feature, String expandedPath, DynamicObject pat
202201
}
203202

204203
private boolean parseAndCall(String feature, String expandedPath) {
205-
final RubySource source;
206-
try {
207-
final FileLoader fileLoader = new FileLoader(getContext());
208-
source = fileLoader.loadFile(expandedPath);
209-
} catch (IOException e) {
210-
return false;
211-
}
212-
213-
final String language = getLanguage(source.getSource());
204+
if (isCExtension(expandedPath)) {
205+
requireCExtension(feature, expandedPath);
206+
} else {
207+
// All other files are assumed to be Ruby, the file type detection is not enough
208+
final RubySource source;
209+
try {
210+
final FileLoader fileLoader = new FileLoader(getContext());
211+
source = fileLoader.loadFile(getContext().getEnv(), expandedPath);
212+
} catch (IOException e) {
213+
return false;
214+
}
214215

215-
if (TruffleRuby.LANGUAGE_ID.equals(language)) {
216216
final RubyRootNode rootNode = getContext().getCodeLoader().parse(
217217
source,
218218
ParserContext.TOP_LEVEL,
@@ -233,14 +233,14 @@ private boolean parseAndCall(String feature, String expandedPath) {
233233
} finally {
234234
requireMetric("after-execute-" + feature);
235235
}
236-
} else if (TruffleRuby.LLVM_ID.equals(language)) {
237-
requireCExtension(feature, expandedPath);
238-
} else {
239-
throw new UnsupportedOperationException();
240236
}
241237
return true;
242238
}
243239

240+
private boolean isCExtension(String path) {
241+
return path.toLowerCase(Locale.ENGLISH).endsWith(RubyLanguage.CEXT_EXTENSION);
242+
}
243+
244244
@TruffleBoundary
245245
private void requireCExtension(String feature, String expandedPath) {
246246
final FeatureLoader featureLoader = getContext().getFeatureLoader();
@@ -374,11 +374,6 @@ private Throwable searchForException(String exceptionClass, Throwable exception)
374374
return null;
375375
}
376376

377-
@TruffleBoundary
378-
private String getLanguage(Source source) {
379-
return source.getLanguage();
380-
}
381-
382377
@TruffleBoundary
383378
private String getBaseName(String path) {
384379
final String name = new File(path).getName();

0 commit comments

Comments
 (0)