Skip to content

Commit 9f057ab

Browse files
author
Adam Hrbac
committed
Change mime format and support all future feats
1 parent 31c5df1 commit 9f057ab

File tree

8 files changed

+156
-73
lines changed

8 files changed

+156
-73
lines changed

graalpython/com.oracle.graal.python.pegparser/src/com/oracle/graal/python/pegparser/FutureFeature.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -40,13 +40,35 @@
4040
*/
4141
package com.oracle.graal.python.pegparser;
4242

43+
import java.util.EnumSet;
44+
4345
public enum FutureFeature {
44-
ANNOTATIONS(0x1000000), // these values are duplicated in BuiltinFunctions.Compiler
46+
ANNOTATIONS(0x1000000),
4547
BARRY_AS_BDFL(0x400000);
4648

4749
public final int flagValue;
50+
public static final int ALL_FLAGS;
51+
52+
private static final FutureFeature[] VALUES = FutureFeature.values();
53+
static {
54+
int flags = 0;
55+
for (FutureFeature feat : VALUES) {
56+
flags |= feat.flagValue;
57+
}
58+
ALL_FLAGS = flags;
59+
}
4860

4961
FutureFeature(int flagValue) {
5062
this.flagValue = flagValue;
5163
}
64+
65+
public static EnumSet<FutureFeature> fromFlags(int flags) {
66+
EnumSet<FutureFeature> set = EnumSet.noneOf(FutureFeature.class);
67+
for (FutureFeature feat : VALUES) {
68+
if ((feat.flagValue & flags) != 0) {
69+
set.add(feat);
70+
}
71+
}
72+
return set;
73+
}
5274
}

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/basic/FutureAnnotationTests.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,43 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
141
package com.oracle.graal.python.test.basic;
242

343
import static com.oracle.graal.python.test.PythonTests.assertPrints;

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/compiler/CompilerTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import com.oracle.graal.python.compiler.CompilationUnit;
5959
import com.oracle.graal.python.compiler.Compiler;
6060
import com.oracle.graal.python.pegparser.ErrorCallback;
61+
import com.oracle.graal.python.pegparser.FutureFeature;
6162
import com.oracle.graal.python.pegparser.InputType;
6263
import com.oracle.graal.python.pegparser.Parser;
6364
import com.oracle.graal.python.pegparser.sst.ModTy;
@@ -1096,7 +1097,7 @@ private static CodeUnit assemble(String src, InputType type) {
10961097
Parser parser = Compiler.createParser(src, errorCallback, type, false);
10971098
ModTy result = (ModTy) parser.parse();
10981099
Compiler compiler = new Compiler(errorCallback);
1099-
CompilationUnit cu = compiler.compile(result, EnumSet.noneOf(Compiler.Flags.class), 2, false);
1100+
CompilationUnit cu = compiler.compile(result, EnumSet.noneOf(Compiler.Flags.class), 2, EnumSet.noneOf(FutureFeature.class));
11001101
return cu.assemble();
11011102
}
11021103

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 74 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@
3535

3636
import java.io.IOException;
3737
import java.nio.file.InvalidPathException;
38+
import java.util.ArrayList;
3839
import java.util.Arrays;
3940
import java.util.EnumSet;
41+
import java.util.HashSet;
4042
import java.util.List;
4143
import java.util.concurrent.ConcurrentHashMap;
4244
import java.util.concurrent.Semaphore;
@@ -74,6 +76,7 @@
7476
import com.oracle.graal.python.nodes.exception.TopLevelExceptionHandler;
7577
import com.oracle.graal.python.nodes.frame.GetFrameLocalsNode;
7678
import com.oracle.graal.python.nodes.frame.MaterializeFrameNode;
79+
import com.oracle.graal.python.pegparser.FutureFeature;
7780
import com.oracle.graal.python.pegparser.InputType;
7881
import com.oracle.graal.python.pegparser.NodeFactory;
7982
import com.oracle.graal.python.pegparser.Parser;
@@ -131,10 +134,12 @@
131134
name = PythonLanguage.NAME, //
132135
implementationName = PythonLanguage.IMPLEMENTATION_NAME, //
133136
version = PythonLanguage.VERSION, //
134-
characterMimeTypes = {PythonLanguage.MIME_TYPE, "text/x-python-eval-0-0", "text/x-python-eval-0-1", "text/x-python-eval-1-0", "text/x-python-eval-1-1", "text/x-python-eval-2-0",
135-
"text/x-python-eval-2-1", "text/x-python-compile-0-0", "text/x-python-compile-0-1", "text/x-python-compile-1-0", "text/x-python-compile-1-1",
136-
"text/x-python-compile-2-0",
137-
"text/x-python-compile-2-1"}, //
137+
characterMimeTypes = {PythonLanguage.MIME_TYPE,
138+
"text/x-python-\0\u0000-eval", "text/x-python-\0\u0000-compile", "text/x-python-\1\u0000-eval", "text/x-python-\1\u0000-compile", "text/x-python-\2\u0000-eval",
139+
"text/x-python-\2\u0000-compile", "text/x-python-\0\u0100-eval", "text/x-python-\0\u0100-compile", "text/x-python-\1\u0100-eval", "text/x-python-\1\u0100-compile",
140+
"text/x-python-\2\u0100-eval", "text/x-python-\2\u0100-compile", "text/x-python-\0\u0040-eval", "text/x-python-\0\u0040-compile", "text/x-python-\1\u0040-eval",
141+
"text/x-python-\1\u0040-compile", "text/x-python-\2\u0040-eval", "text/x-python-\2\u0040-compile", "text/x-python-\0\u0140-eval", "text/x-python-\0\u0140-compile",
142+
"text/x-python-\1\u0140-eval", "text/x-python-\1\u0140-compile", "text/x-python-\2\u0140-eval", "text/x-python-\2\u0140-compile"}, //
138143
byteMimeTypes = {PythonLanguage.MIME_TYPE_BYTECODE}, //
139144
defaultMimeType = PythonLanguage.MIME_TYPE, //
140145
dependentLanguages = {"nfi", "llvm"}, //
@@ -209,17 +214,44 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
209214
public static final String MIME_TYPE = "text/x-python";
210215

211216
// the syntax for mime types is as follows
212-
// <mime> ::= "text/x-python-" <kind> "-" <optlevel> "-" <future_annotations>
217+
// <mime> ::= "text/x-python-" <optlevel> <flags> "-" kind
213218
// <kind> ::= "compile" | "eval"
214-
// <optlevel> ::= "0" | "1" | "2"
215-
// <future_annotations> ::= "0" | "1"
219+
// <optlevel> ::= "\0" | "\1" | "\2"
220+
// <flags> ::= "\u0040" | "\u0100" | "\u0140" | "\u0000"
221+
// where 0100 implies annotations, and 0040 implies barry_as_flufl
216222
static final String MIME_PREFIX = MIME_TYPE + "-";
223+
static final int OPT_FLAGS_LEN = 2; // 1 char is optlevel, 1 char is flags
217224
static final String MIME_KIND_COMPILE = "compile";
218225
static final String MIME_KIND_EVAL = "eval";
219-
static final int MIME_KIND_IDX = 0;
220-
static final int MIME_OPTLEVEL_IDX = 1;
221-
static final int MIME_FUTURE_ANNOTATIONS_IDX = 2;
222-
static final int MIME_FIELD_COUNT = 3;
226+
// Since flags are greater than the highest unicode codepoint, we shift them into more
227+
// reasonable values in the mime type. 4 hex digits
228+
static final int MIME_FLAG_SHIFTBY = 4 * 4;
229+
// a dash follows after the opt flag pair
230+
static final int MIME_KIND_START = MIME_PREFIX.length() + OPT_FLAGS_LEN + 1;
231+
232+
static {
233+
ArrayList<String> mimeTypes = new ArrayList<>();
234+
ArrayList<String> mimeJavaStrings = new ArrayList<>();
235+
FutureFeature[] all = FutureFeature.values();
236+
for (int flagset = 0; flagset < (1 << all.length); ++flagset) {
237+
int flags = 0;
238+
for (int i = 0; i < all.length; ++i) {
239+
if ((flagset & (1 << i)) != 0) {
240+
flags |= all[i].flagValue;
241+
}
242+
}
243+
for (int opt = 0; opt <= 2; opt++) {
244+
for (String typ : new String[]{MIME_KIND_EVAL, MIME_KIND_COMPILE}) {
245+
mimeTypes.add(MIME_PREFIX + optFlagsToMime(opt, flags) + "-" + typ);
246+
mimeJavaStrings.add(String.format("\"%s\\%d\\u%04x-%s\"", MIME_PREFIX, opt, flags >> MIME_FLAG_SHIFTBY, typ));
247+
}
248+
}
249+
}
250+
HashSet<String> currentMimeTypes = new HashSet<>(List.of(PythonLanguage.class.getAnnotation(Registration.class).characterMimeTypes()));
251+
if (!currentMimeTypes.containsAll(mimeTypes)) {
252+
assert false : "Expected all of {" + String.join(", ", mimeJavaStrings) + "} in the PythonLanguage characterMimeTypes";
253+
}
254+
}
223255

224256
public static final String MIME_TYPE_BYTECODE = "application/x-python-bytecode";
225257

@@ -395,29 +427,26 @@ protected void initializeContext(PythonContext context) {
395427
context.initialize();
396428
}
397429

398-
public static String getCompileMimeType(int optimize, boolean futureAnnotations) {
399-
String futureAnnField = futureAnnotations ? "1" : "0";
400-
String compile = MIME_PREFIX + "compile-";
401-
if (optimize <= 0) {
402-
return compile + "0-" + futureAnnField;
403-
} else if (optimize == 1) {
404-
return compile + "1-" + futureAnnField;
405-
} else {
406-
return compile + "2-" + futureAnnField;
430+
private static String optFlagsToMime(int optimize, int flags) {
431+
if (optimize < 0) {
432+
optimize = 0;
433+
} else if (optimize > 2) {
434+
optimize = 2;
407435
}
436+
String optField = new String(new byte[]{(byte) optimize});
437+
String flagField = new String(new int[]{(flags & FutureFeature.ALL_FLAGS) >> MIME_FLAG_SHIFTBY}, 0, 1);
438+
assert flagField.length() == 1 : "flags in mime type ended up a surrogate";
439+
return optField + flagField;
408440
}
409441

410-
// TODO inherit other implemented
411-
public static String getEvalMimeType(int optimize, boolean futureAnnotations) {
412-
String futureAnnField = futureAnnotations ? "1" : "0";
413-
String eval = MIME_PREFIX + "eval-";
414-
if (optimize <= 0) {
415-
return eval + "0-" + futureAnnField;
416-
} else if (optimize == 1) {
417-
return eval + "1-" + futureAnnField;
418-
} else {
419-
return eval + "2-" + futureAnnField;
420-
}
442+
public static String getCompileMimeType(int optimize, int flags) {
443+
String optFlags = optFlagsToMime(optimize, flags);
444+
return MIME_PREFIX + optFlags + "-compile";
445+
}
446+
447+
public static String getEvalMimeType(int optimize, int flags) {
448+
String optFlags = optFlagsToMime(optimize, flags);
449+
return MIME_PREFIX + optFlags + "-eval";
421450
}
422451

423452
@Override
@@ -429,7 +458,7 @@ protected CallTarget parse(ParsingRequest request) {
429458
throw new IllegalStateException("parse with arguments not allowed for interactive sources");
430459
}
431460
InputType inputType = source.isInteractive() ? InputType.SINGLE : InputType.FILE;
432-
return parse(context, source, inputType, true, 0, source.isInteractive(), request.getArgumentNames(), false);
461+
return parse(context, source, inputType, true, 0, source.isInteractive(), request.getArgumentNames(), EnumSet.noneOf(FutureFeature.class));
433462
}
434463
if (!request.getArgumentNames().isEmpty()) {
435464
throw new IllegalStateException("parse with arguments is only allowed for " + MIME_TYPE + " mime type");
@@ -468,34 +497,24 @@ protected CallTarget parse(ParsingRequest request) {
468497
String mime = source.getMimeType();
469498
String prefix = mime.substring(0, MIME_PREFIX.length());
470499
if (!prefix.equals(MIME_PREFIX)) {
471-
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
472-
}
473-
String[] fields = mime.substring(MIME_PREFIX.length()).split("-");
474-
if (fields.length != MIME_FIELD_COUNT) {
475-
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
500+
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + mime);
476501
}
477-
String kind = fields[MIME_KIND_IDX];
502+
String kind = mime.substring(MIME_KIND_START);
478503
InputType type;
479504
if (kind.equals(MIME_KIND_COMPILE)) {
480505
type = InputType.FILE;
481506
} else if (kind.equals(MIME_KIND_EVAL)) {
482507
type = InputType.EVAL;
483508
} else {
484-
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
485-
}
486-
int optimize;
487-
int futureAnnotations;
488-
try {
489-
optimize = Integer.parseInt(fields[MIME_OPTLEVEL_IDX]);
490-
futureAnnotations = Integer.parseInt(fields[MIME_FUTURE_ANNOTATIONS_IDX]);
491-
} catch (NumberFormatException e) {
492-
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
509+
throw CompilerDirectives.shouldNotReachHere("unknown compilation kind: " + kind + " from mime type: " + mime);
493510
}
494-
if (0 > optimize || optimize > 2 || 0 > futureAnnotations || futureAnnotations > 2) {
495-
throw CompilerDirectives.shouldNotReachHere("unknown mime type: " + source.getMimeType());
511+
int optimize = mime.codePointAt(MIME_PREFIX.length());
512+
int flags = mime.codePointAt(MIME_PREFIX.length() + 1) << MIME_FLAG_SHIFTBY;
513+
if (0 > optimize || optimize > 2 || (flags & ~FutureFeature.ALL_FLAGS) != 0) {
514+
throw CompilerDirectives.shouldNotReachHere("Invalid value for optlevel or flags: " + optimize + "," + flags + " from mime type: " + mime);
496515
}
497516
assert !source.isInteractive();
498-
return parse(context, source, type, false, optimize, false, null, futureAnnotations == 1);
517+
return parse(context, source, type, false, optimize, false, null, FutureFeature.fromFlags(flags));
499518
}
500519

501520
private static Source tryLoadSource(PythonContext context, CodeUnit code, boolean internal, String path) {
@@ -507,13 +526,13 @@ private static Source tryLoadSource(PythonContext context, CodeUnit code, boolea
507526
}
508527

509528
public RootCallTarget parse(PythonContext context, Source source, InputType type, boolean topLevel, int optimize, boolean interactiveTerminal, List<String> argumentNames,
510-
boolean futureAnnotations) {
529+
EnumSet<FutureFeature> futureFeatures) {
511530
RaisePythonExceptionErrorCallback errorCb = new RaisePythonExceptionErrorCallback(source, PythonOptions.isPExceptionWithJavaStacktrace(this));
512531
try {
513532
Parser parser = Compiler.createParser(source.getCharacters().toString(), errorCb, type, interactiveTerminal);
514533
ModTy mod = (ModTy) parser.parse();
515534
assert mod != null;
516-
return compileForBytecodeInterpreter(context, mod, source, topLevel, optimize, argumentNames, errorCb, futureAnnotations);
535+
return compileForBytecodeInterpreter(context, mod, source, topLevel, optimize, argumentNames, errorCb, futureFeatures);
517536
} catch (PException e) {
518537
if (topLevel) {
519538
PythonUtils.getOrCreateCallTarget(new TopLevelExceptionHandler(this, e)).call();
@@ -524,7 +543,7 @@ public RootCallTarget parse(PythonContext context, Source source, InputType type
524543

525544
@TruffleBoundary
526545
public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy mod, Source source, boolean topLevel, int optimize, List<String> argumentNames,
527-
RaisePythonExceptionErrorCallback errorCallback, boolean futureAnnotations) {
546+
RaisePythonExceptionErrorCallback errorCallback, EnumSet<FutureFeature> futureFeatures) {
528547
RaisePythonExceptionErrorCallback errorCb = errorCallback;
529548
if (errorCb == null) {
530549
errorCb = new RaisePythonExceptionErrorCallback(source, PythonOptions.isPExceptionWithJavaStacktrace(this));
@@ -535,7 +554,7 @@ public RootCallTarget compileForBytecodeInterpreter(PythonContext context, ModTy
535554
if (hasArguments) {
536555
mod = transformASTForExecutionWithArguments(argumentNames, mod);
537556
}
538-
CompilationUnit cu = compiler.compile(mod, EnumSet.noneOf(Compiler.Flags.class), optimize, futureAnnotations);
557+
CompilationUnit cu = compiler.compile(mod, EnumSet.noneOf(Compiler.Flags.class), optimize, futureFeatures);
539558
CodeUnit co = cu.assemble();
540559
RootNode rootNode = PBytecodeRootNode.create(this, co, source, errorCb);
541560
if (topLevel) {
@@ -620,7 +639,7 @@ public String getName() {
620639
@Override
621640
public ExecutableNode parse(InlineParsingRequest request) {
622641
PythonContext context = PythonContext.get(null);
623-
RootCallTarget callTarget = parse(context, request.getSource(), InputType.EVAL, false, 0, false, null, false);
642+
RootCallTarget callTarget = parse(context, request.getSource(), InputType.EVAL, false, 0, false, null, EnumSet.noneOf(FutureFeature.class));
624643
return new ExecutableNode(this) {
625644
@Child private GilNode gilNode = GilNode.create();
626645
@Child private GenericInvokeNode invokeNode = GenericInvokeNode.create();

0 commit comments

Comments
 (0)