Skip to content

Commit 98555b9

Browse files
committed
[GR-12240] Add an option --python.OpaqueFilesystem that always returns a TruffleObject from os.reads. These objects work for compilation (and thus imports), but nothing else.
1 parent 1f7ac2f commit 98555b9

File tree

6 files changed

+143
-2
lines changed

6 files changed

+143
-2
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import com.oracle.graal.python.builtins.objects.PNone;
8383
import com.oracle.graal.python.builtins.objects.PNotImplemented;
8484
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
85+
import com.oracle.graal.python.builtins.objects.bytes.OpaqueBytes;
8586
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
8687
import com.oracle.graal.python.builtins.objects.bytes.PIBytesLike;
8788
import com.oracle.graal.python.builtins.objects.cell.PCell;
@@ -601,6 +602,12 @@ Object compile(PBytes source, String filename, String mode, Object kwFlags, Obje
601602
return compile(new String(toBytesNode.execute(source)), filename, mode, kwFlags, kwDontInherit, kwOptimize);
602603
}
603604

605+
@Specialization
606+
@TruffleBoundary
607+
Object compile(OpaqueBytes source, String filename, String mode, Object kwFlags, Object kwDontInherit, Object kwOptimize) {
608+
return compile(new String(source.getBytes()), filename, mode, kwFlags, kwDontInherit, kwOptimize);
609+
}
610+
604611
@SuppressWarnings("unused")
605612
@Specialization
606613
@TruffleBoundary

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltinsFactory.ConvertPathlikeObjectNodeGen;
6767
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltinsFactory.StatNodeFactory;
6868
import com.oracle.graal.python.builtins.objects.PNone;
69+
import com.oracle.graal.python.builtins.objects.bytes.OpaqueBytes;
6970
import com.oracle.graal.python.builtins.objects.bytes.PByteArray;
7071
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
7172
import com.oracle.graal.python.builtins.objects.bytes.PIBytesLike;
@@ -801,7 +802,21 @@ public static WriteNode create() {
801802
@GenerateNodeFactory
802803
@TypeSystemReference(PythonArithmeticTypes.class)
803804
public abstract static class ReadNode extends PythonFileNode {
804-
@Specialization
805+
@Specialization(guards = "readOpaque()")
806+
@TruffleBoundary
807+
Object readOpaque(int fd, @SuppressWarnings("unused") Object requestedSize) {
808+
SeekableByteChannel channel = getFileChannel(fd);
809+
try {
810+
long size = channel.size() - channel.position();
811+
ByteBuffer dst = ByteBuffer.allocate((int) size);
812+
channel.read(dst);
813+
return new OpaqueBytes(dst.array());
814+
} catch (IOException e) {
815+
throw raise(OSError, e.getMessage());
816+
}
817+
}
818+
819+
@Specialization(guards = "!readOpaque()")
805820
@TruffleBoundary
806821
Object read(int fd, long requestedSize) {
807822
SeekableByteChannel channel = getFileChannel(fd);
@@ -817,6 +832,10 @@ Object read(int fd, long requestedSize) {
817832
throw raise(OSError, e.getMessage());
818833
}
819834
}
835+
836+
protected boolean readOpaque() {
837+
return OpaqueBytes.isEnabled(getContext());
838+
}
820839
}
821840

822841
@Builtin(name = "isatty", fixedNumOfPositionalArgs = 1)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesBuiltins.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
5757
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.NormalizeIndexNode;
5858
import com.oracle.graal.python.builtins.objects.iterator.PSequenceIterator;
59+
import com.oracle.graal.python.builtins.objects.list.PList;
5960
import com.oracle.graal.python.nodes.PNodeWithContext;
6061
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
6162
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
@@ -295,7 +296,30 @@ public Object repr(PBytes self,
295296
@Builtin(name = "join", fixedNumOfPositionalArgs = 2)
296297
@GenerateNodeFactory
297298
public abstract static class JoinNode extends PythonBinaryBuiltinNode {
298-
@Specialization
299+
protected boolean readOpaque() {
300+
return OpaqueBytes.isEnabled(getContext());
301+
}
302+
303+
@Specialization(guards = {"readOpaque()"})
304+
public Object join(PBytes bytes, PList iterable,
305+
@Cached("create()") SequenceStorageNodes.GetItemNode getItemNode,
306+
@Cached("create()") SequenceStorageNodes.LenNode lenNode,
307+
@Cached("create()") SequenceStorageNodes.ToByteArrayNode toByteArrayNode,
308+
@Cached("create()") BytesNodes.BytesJoinNode bytesJoinNode) {
309+
int len = lenNode.execute(iterable.getSequenceStorage());
310+
if (len == 1) {
311+
// branch profiles aren't really needed, because of the specialization
312+
// happening in the getItemNode on first execution and the assumption
313+
// in OpaqueBytes.isInstance
314+
Object firstItem = getItemNode.execute(iterable.getSequenceStorage(), 0);
315+
if (OpaqueBytes.isInstance(firstItem)) {
316+
return firstItem;
317+
}
318+
}
319+
return join(bytes, iterable, toByteArrayNode, bytesJoinNode);
320+
}
321+
322+
@Specialization(guards = {"!readOpaque()"})
299323
public PBytes join(PBytes bytes, Object iterable,
300324
@Cached("create()") SequenceStorageNodes.ToByteArrayNode toByteArrayNode,
301325
@Cached("create()") BytesNodes.BytesJoinNode bytesJoinNode) {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.oracle.graal.python.builtins.objects.bytes;
2+
3+
import com.oracle.graal.python.runtime.PythonContext;
4+
import com.oracle.graal.python.runtime.PythonOptions;
5+
import com.oracle.truffle.api.Assumption;
6+
import com.oracle.truffle.api.CompilerDirectives;
7+
import com.oracle.truffle.api.Truffle;
8+
import com.oracle.truffle.api.interop.CanResolve;
9+
import com.oracle.truffle.api.interop.ForeignAccess;
10+
import com.oracle.truffle.api.interop.MessageResolution;
11+
import com.oracle.truffle.api.interop.Resolve;
12+
import com.oracle.truffle.api.interop.TruffleObject;
13+
import com.oracle.truffle.api.nodes.Node;
14+
15+
public final class OpaqueBytes implements TruffleObject {
16+
private static final Assumption neverOpaqueAssumption = Truffle.getRuntime().createAssumption("all contexts use a readable filesystem");
17+
private static final Assumption alwaysOpaqueAssumption = Truffle.getRuntime().createAssumption("no context has a readable filesystem");
18+
private final byte[] bytes;
19+
20+
public OpaqueBytes(byte[] bytes) {
21+
assert !neverOpaqueAssumption.isValid();
22+
this.bytes = bytes;
23+
}
24+
25+
public ForeignAccess getForeignAccess() {
26+
return OpaqueBytesMessageResolutionForeign.ACCESS;
27+
}
28+
29+
public byte[] getBytes() {
30+
return bytes;
31+
}
32+
33+
public static boolean isInstance(Object next) {
34+
if (neverOpaqueAssumption.isValid()) {
35+
return false;
36+
}
37+
return next instanceof OpaqueBytes;
38+
}
39+
40+
public static boolean isEnabled(PythonContext context) {
41+
if (neverOpaqueAssumption.isValid()) {
42+
return false;
43+
} else if (alwaysOpaqueAssumption.isValid()) {
44+
return true;
45+
}
46+
return checkOption(context);
47+
}
48+
49+
private static Boolean checkOption(PythonContext context) {
50+
return PythonOptions.getOption(context, PythonOptions.OpaqueFilesystem);
51+
}
52+
53+
public static void initializeForNewContext(PythonContext context) {
54+
CompilerDirectives.transferToInterpreter();
55+
if (checkOption(context)) {
56+
neverOpaqueAssumption.invalidate();
57+
} else {
58+
alwaysOpaqueAssumption.invalidate();
59+
}
60+
}
61+
62+
@MessageResolution(receiverType = OpaqueBytes.class)
63+
static class OpaqueBytesMessageResolution {
64+
@Resolve(message = "HAS_SIZE")
65+
abstract static class HasSizeNode extends Node {
66+
Object access(@SuppressWarnings("unused") OpaqueBytes object) {
67+
return true;
68+
}
69+
}
70+
71+
@Resolve(message = "GET_SIZE")
72+
abstract static class SizeNode extends Node {
73+
Object access(OpaqueBytes object) {
74+
return object.bytes.length;
75+
}
76+
}
77+
78+
@CanResolve
79+
abstract static class CheckFunction extends Node {
80+
protected static boolean test(TruffleObject receiver) {
81+
return receiver instanceof OpaqueBytes;
82+
}
83+
}
84+
}
85+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.graalvm.options.OptionValues;
4040

4141
import com.oracle.graal.python.PythonLanguage;
42+
import com.oracle.graal.python.builtins.objects.bytes.OpaqueBytes;
4243
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
4344
import com.oracle.graal.python.builtins.objects.dict.PDict;
4445
import com.oracle.graal.python.builtins.objects.module.PythonModule;
@@ -200,6 +201,7 @@ private void setupRuntimeInformation() {
200201
mainModule = core.factory().createPythonModule(__MAIN__);
201202
mainModule.setAttribute(__BUILTINS__, builtinsModule);
202203
sysModules.setItem(__MAIN__, mainModule);
204+
OpaqueBytes.initializeForNewContext(this);
203205
currentException = null;
204206
isInitialized = true;
205207
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ private PythonOptions() {
8282
@Option(category = OptionCategory.EXPERT, help = "This option is set by the Python launcher to tell the language it can print exceptions directly") //
8383
public static final OptionKey<Boolean> AlwaysRunExcepthook = new OptionKey<>(false);
8484

85+
@Option(category = OptionCategory.USER, help = "This option makes reading from files return opaque objects. Imports can work with such data, " +
86+
"but all other access to the contents of the file is disabled, so the files are kept secret.") //
87+
public static final OptionKey<Boolean> OpaqueFilesystem = new OptionKey<>(false);
88+
8589
@Option(category = OptionCategory.USER, help = "") //
8690
public static final OptionKey<Boolean> InspectFlag = new OptionKey<>(false);
8791

0 commit comments

Comments
 (0)