Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-38700] Migrate to the Bytecode DSL #384

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion ci.jsonnet
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{ "overlay": "4832d2526e48416dc03d1ecc9b56986dc00f6d57" }
{ "overlay": "ff81810133286bdf50b6090cbf448ae2ac6790ca" }
Original file line number Diff line number Diff line change
@@ -501,6 +501,9 @@ def run_benchmark(args):
else:
print("### no extra module search paths specified")

if GRAALPYTHON:
print(f"### using bytecode DSL interpreter: {__graalpython__.is_bytecode_dsl_interpreter}")

BenchRunner(bench_file, bench_args=bench_args, iterations=iterations, warmup=warmup, warmup_runs=warmup_runs, startup=startup, live_results=live_results).run()


24 changes: 15 additions & 9 deletions graalpython/com.oracle.graal.python.frozen/freeze_modules.py
Original file line number Diff line number Diff line change
@@ -155,7 +155,7 @@ def relpath_for_posix_display(path, base):
#######################################
# specs

def parse_frozen_specs():
def parse_frozen_specs(suffix):
seen = {}
for section, specs in FROZEN:
parsed = _parse_specs(specs, section, seen)
@@ -164,7 +164,7 @@ def parse_frozen_specs():
try:
source = seen[frozenid]
except KeyError:
source = FrozenSource.from_id(frozenid, pyfile)
source = FrozenSource.from_id(frozenid, suffix, pyfile)
seen[frozenid] = source
else:
assert not pyfile or pyfile == source.pyfile, item
@@ -272,11 +272,11 @@ def iter_subs():
class FrozenSource(namedtuple('FrozenSource', 'id pyfile frozenfile deepfreezefile')):

@classmethod
def from_id(cls, frozenid, pyfile=None):
def from_id(cls, frozenid, suffix, pyfile=None):
if not pyfile:
pyfile = os.path.join(STDLIB_DIR, *frozenid.split('.')) + '.py'
#assert os.path.exists(pyfile), (frozenid, pyfile)
frozenfile = resolve_frozen_file(frozenid, FROZEN_MODULES_DIR)
frozenfile = resolve_frozen_file(frozenid, FROZEN_MODULES_DIR, suffix)
return cls(frozenid, pyfile, frozenfile, STDLIB_DIR)

@classmethod
@@ -312,7 +312,7 @@ def isbootstrap(self):
return self.id in BOOTSTRAP


def resolve_frozen_file(frozenid, destdir):
def resolve_frozen_file(frozenid, destdir, suffix):
"""Return the filename corresponding to the given frozen ID.

For stdlib modules the ID will always be the full name
@@ -325,7 +325,7 @@ def resolve_frozen_file(frozenid, destdir):
raise ValueError(f'unsupported frozenid {frozenid!r}')
# We use a consistent naming convention for all frozen modules.
frozen_symbol = FrozenSource.resolve_symbol(frozenid)
frozenfile = f"Frozen{frozen_symbol}.bin"
frozenfile = f"Frozen{frozen_symbol}.{suffix}"

if not destdir:
return frozenfile
@@ -635,11 +635,17 @@ def main():
STDLIB_DIR = os.path.abspath(parsed_args.python_lib)
FROZEN_MODULES_DIR = os.path.abspath(parsed_args.binary_dir)

if __graalpython__.is_bytecode_dsl_interpreter:
suffix = "bin_dsl"
assert os.path.isdir(parsed_args.binary_dir), "Frozen modules for the DSL should be built after the manual bytecode interpreter."
else:
suffix = "bin"
shutil.rmtree(parsed_args.binary_dir, ignore_errors=True)
os.makedirs(parsed_args.binary_dir)

# create module specs
modules = list(parse_frozen_specs())
modules = list(parse_frozen_specs(suffix))

shutil.rmtree(parsed_args.binary_dir, ignore_errors=True)
os.makedirs(parsed_args.binary_dir)
# write frozen module binary files containing the byte code and class files
# used for importing the binary files
for src in _iter_sources(modules):
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -41,8 +41,10 @@

package com.oracle.graal.python.pegparser.test;

import org.junit.Ignore;
import org.junit.Test;

@Ignore // GR-62729
public class LambdaInFunctionTests extends ParserTestBase {

@Test
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -41,6 +41,7 @@

package com.oracle.graal.python.pegparser.test;

import org.junit.Ignore;
import org.junit.Test;

public class YieldStatementTests extends ParserTestBase {
@@ -148,6 +149,7 @@ public void customIter01() throws Exception {
}

@Test
@Ignore // GR-62729
public void yield17() throws Exception {
checkScopeAndTree("generator = type((lambda: (yield))())");
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -229,11 +229,15 @@ public boolean isNested() {
}

public HashMap<String, Integer> getSymbolsByType(EnumSet<DefUse> expectedFlags, int start) {
return getSymbolsByType(expectedFlags, EnumSet.noneOf(DefUse.class), start);
}

public HashMap<String, Integer> getSymbolsByType(EnumSet<DefUse> expectedFlags, EnumSet<DefUse> unexpectedFlags, int start) {
int i = start;
HashMap<String, Integer> mapping = new HashMap<>();
for (String key : getSortedSymbols()) {
EnumSet<DefUse> keyFlags = getUseOfName(key);
if (!Collections.disjoint(expectedFlags, keyFlags)) {
if (!Collections.disjoint(expectedFlags, keyFlags) && Collections.disjoint(unexpectedFlags, keyFlags)) {
mapping.put(key, i++);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
@@ -98,6 +98,7 @@ public class ScopeEnvironment {
final HashMap<SSTNode, Scope> blocks = new HashMap<>();
final ErrorCallback errorCallback;
final EnumSet<FutureFeature> futureFeatures;
final HashMap<Scope, Scope> parents = new HashMap<>();

public static ScopeEnvironment analyze(ModTy moduleNode, ErrorCallback errorCallback, EnumSet<FutureFeature> futureFeatures) {
return new ScopeEnvironment(moduleNode, errorCallback, futureFeatures);
@@ -128,6 +129,14 @@ public Scope lookupScope(SSTNode node) {
return blocks.get(node);
}

public Scope lookupParent(Scope scope) {
return parents.get(scope);
}

public Scope getTopScope() {
return topScope;
}

private void analyzeBlock(Scope scope, HashSet<String> bound, HashSet<String> free, HashSet<String> global) {
HashSet<String> local = new HashSet<>();
HashMap<String, DefUse> scopes = new HashMap<>();
@@ -328,6 +337,7 @@ private void enterBlock(String name, Scope.ScopeType type, SSTNode ast) {
if (type == Scope.ScopeType.Annotation) {
return;
}
env.parents.put(scope, prev);
if (prev != null) {
prev.children.add(scope);
}
@@ -635,7 +645,7 @@ public Void visit(ExprTy.Lambda node) {
visitSequence(node.args.defaults);
visitSequence(node.args.kwDefaults);
}
enterBlock("lambda", ScopeType.Function, node);
enterBlock("<lambda>", ScopeType.Function, node);
try {
if (node.args != null) {
node.args.accept(this);
Original file line number Diff line number Diff line change
@@ -420,6 +420,11 @@ protected List<String> preprocessArguments(List<String> givenArgs, Map<String, S
}
}

if (!ImageInfo.inImageCode() && Boolean.getBoolean("python.EnableBytecodeDSLInterpreter")) {
// forward the property on JVM
addRelaunchArg("--vm.Dpython.EnableBytecodeDSLInterpreter=true");
}

// According to CPython if no arguments are given, they contain an empty string.
if (programArgs.isEmpty()) {
programArgs.add("");
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2023, Oracle and/or its affiliates.
* Copyright (c) 2017, 2025, Oracle and/or its affiliates.
* Copyright (c) 2013, Regents of the University of California
*
* All rights reserved.
@@ -72,7 +72,7 @@ public void desugared() {
}

@Test
public void testYieldFrom() {
public void testYieldFromSimple() {
String source = "def gen1():\n" +
" yield 1\n" +
" yield 2\n" +
@@ -83,4 +83,119 @@ public void testYieldFrom() {
"print(list(gen2()))\n";
assertPrints("[1, 2]\n", source);
}

@Test
public void testYieldFromIterable() {
// yield from should extract an iterator from a non-generator argument
String source = "class Foo:\n" +
" def __init__(self, wrapped):\n" +
" self.wrapped = wrapped\n" +
" def __iter__(self):\n" +
" return iter(self.wrapped)\n" +
"def gen():\n" +
" foo = Foo([1,2,3])\n" +
" yield from foo\n" +
"\n" +
"print(list(gen()))\n";
assertPrints("[1, 2, 3]\n", source);
}

@Test
public void testYieldFromReturn() {
String source = "def gen1():\n" +
" yield 1\n" +
" yield 2\n" +
" return 3\n" +
"\n" +
"def gen2():\n" +
" final = yield from gen1()\n" +
" yield final\n" +
"\n" +
"print(list(gen2()))\n";
assertPrints("[1, 2, 3]\n", source);
}

@Test
public void testYieldFromSend() {
String source = "def gen1(x):\n" +
" yield (yield (yield x))\n" +
"\n" +
"def gen2():\n" +
" yield from gen1(2)\n" +
" yield 8\n" +
"\n" +
"gen = gen2()\n" +
"print(gen.send(None))\n" +
"print(gen.send(4))\n" +
"print(gen.send(6))\n" +
"print(gen.send(42))\n";
assertPrints("2\n4\n6\n8\n", source);
}

@Test
public void testYieldFromThrowCaught() {
String source = "def gen1():\n" +
" try:\n" +
" x = 1\n" +
" while True:\n" +
" x = yield x\n" +
" except ValueError:\n" +
" yield 42\n" +
"\n" +
"def gen2():\n" +
" yield from gen1()\n" +
"\n" +
"gen = gen2()\n" +
"print(gen.send(None))\n" +
"print(gen.send(2))\n" +
"print(gen.send(3))\n" +
"print(gen.throw(ValueError))\n";
assertPrints("1\n2\n3\n42\n", source);
}

@Test
public void testYieldFromThrowUncaught() {
String source = "def gen1():\n" +
" x = 1\n" +
" while True:\n" +
" x = yield x\n" +
"\n" +
"def gen2():\n" +
" yield from gen1()\n" +
"\n" +
"gen = gen2()\n" +
"print(gen.send(None))\n" +
"print(gen.send(2))\n" +
"print(gen.send(3))\n" +
"try:\n" +
" gen.throw(ValueError)\n" +
" print('error')\n" +
"except ValueError:\n" +
" print('success')\n";
assertPrints("1\n2\n3\nsuccess\n", source);
}

@Test
public void testYieldFromClose() {
String source = "def gen1():\n" +
" x = 1\n" +
" try:\n" +
" while True:\n" +
" x = yield x\n" +
" except GeneratorExit:\n" +
" print('gen1 exit')\n" +
"\n" +
"def gen2():\n" +
" try:\n" +
" yield from gen1()\n" +
" except GeneratorExit:\n" +
" print('gen2 exit')\n" +
"\n" +
"gen = gen2()\n" +
"print(gen.send(None))\n" +
"print(gen.send(2))\n" +
"print(gen.send(3))\n" +
"gen.close()\n";
assertPrints("1\n2\n3\ngen1 exit\ngen2 exit\n", source);
}
}
Loading
Oops, something went wrong.