diff --git a/ci.jsonnet b/ci.jsonnet
index 4848ebf505..d255fd0ae9 100644
--- a/ci.jsonnet
+++ b/ci.jsonnet
@@ -118,10 +118,6 @@
// -----------------------------------------------------------------------------------------------------------------
local gate_task_dict = {
"python-unittest": gpgate + platform_spec(no_jobs) + platform_spec({
- "linux:amd64:jdk21" : daily + t("01:00:00") + provide(GPY_JVM21_STANDALONE),
- "linux:aarch64:jdk21" : daily + t("02:00:00") + provide(GPY_JVM21_STANDALONE),
- "darwin:aarch64:jdk21" : daily + t("01:30:00") + provide(GPY_JVM21_STANDALONE),
- "windows:amd64:jdk21" : daily + t("01:30:00") + provide(GPY_JVM21_STANDALONE),
"linux:amd64:jdk-latest" : tier2 + require(GPY_JVM_STANDALONE),
"linux:aarch64:jdk-latest" : tier3 + provide(GPY_JVM_STANDALONE),
"darwin:aarch64:jdk-latest" : tier3 + provide(GPY_JVM_STANDALONE),
@@ -136,10 +132,6 @@
"darwin:aarch64:jdk-latest" : daily + t("01:00:00"),
}),
"python-unittest-multi-context": gpgate + platform_spec(no_jobs) + platform_spec({
- "linux:amd64:jdk21" : daily + t("01:00:00") + require(GPY_JVM21_STANDALONE),
- "linux:aarch64:jdk21" : daily + t("01:30:00") + require(GPY_JVM21_STANDALONE),
- "darwin:aarch64:jdk21" : daily + t("01:00:00") + require(GPY_JVM21_STANDALONE),
- "windows:amd64:jdk21" : daily + t("02:00:00"),
"linux:amd64:jdk-latest" : tier2 + require(GPY_JVM_STANDALONE),
"linux:aarch64:jdk-latest" : daily + t("01:30:00") + require(GPY_JVM_STANDALONE),
"darwin:aarch64:jdk-latest" : daily + t("01:00:00") + require(GPY_JVM_STANDALONE),
@@ -160,10 +152,6 @@
"linux:amd64:jdk-latest" : tier2,
}),
"python-unittest-standalone": gpgate_maven + platform_spec(no_jobs) + platform_spec({
- "linux:amd64:jdk21" : daily + t("02:00:00") + require(GPY_JVM21_STANDALONE),
- "linux:aarch64:jdk21" : daily + t("02:00:00") + require(GPY_JVM21_STANDALONE),
- "darwin:aarch64:jdk21" : daily + t("02:00:00") + require(GPY_JVM21_STANDALONE),
- "windows:amd64:jdk21" : daily + t("02:00:00") + require(GPY_JVM21_STANDALONE) + batches(2),
"linux:amd64:jdk-latest" : tier3 + require(GPY_JVM_STANDALONE) + require(GRAAL_JDK_LATEST),
"linux:aarch64:jdk-latest" : tier3 + require(GPY_JVM_STANDALONE) + require(GRAAL_JDK_LATEST),
"darwin:aarch64:jdk-latest" : tier3 + require(GPY_JVM_STANDALONE) + require(GRAAL_JDK_LATEST),
@@ -244,17 +232,17 @@
"windows:amd64:jdk-latest" : weekly + t("20:00:00") + require(GPY_NATIVE_STANDALONE),
}),
"python-coverage-jacoco-tagged": cov_jacoco_tagged + batches(COVERAGE_SPLIT) + platform_spec(no_jobs) + platform_spec({
- "linux:amd64:jdk21" : weekly + t("20:00:00"),
- "darwin:aarch64:jdk21" : weekly + t("20:00:00"),
- "windows:amd64:jdk21" : weekly + t("20:00:00"),
+ "linux:amd64:jdk-latest" : weekly + t("20:00:00"),
+ "darwin:aarch64:jdk-latest" : weekly + t("20:00:00"),
+ "windows:amd64:jdk-latest" : weekly + t("20:00:00"),
}),
"python-coverage-jacoco-base": cov_jacoco_base + platform_spec(no_jobs) + platform_spec({
- "linux:amd64:jdk21" : weekly + t("20:00:00"),
- "darwin:aarch64:jdk21" : weekly + t("20:00:00"),
- "windows:amd64:jdk21" : weekly + t("20:00:00"),
+ "linux:amd64:jdk-latest" : weekly + t("20:00:00"),
+ "darwin:aarch64:jdk-latest" : weekly + t("20:00:00"),
+ "windows:amd64:jdk-latest" : weekly + t("20:00:00"),
}),
"python-coverage-truffle": cov_truffle + platform_spec(no_jobs) + platform_spec({
- "linux:amd64:jdk21" : weekly + t("20:00:00"),
+ "linux:amd64:jdk-latest" : weekly + t("20:00:00"),
}),
"corp-compliance-watchdog": watchdog + platform_spec(no_jobs) + platform_spec({
"linux:amd64:jdk-latest" : tier1,
@@ -274,10 +262,20 @@
}),
// tests with sandboxed backends for various modules (posix, sha3, compression, pyexpat, ...)
"python-unittest-sandboxed": gpgate_ee + platform_spec(no_jobs) + platform_spec({
+ "linux:amd64:jdk21" : daily + t("01:00:00") + provide(GPY_JVM21_STANDALONE),
+ "linux:aarch64:jdk21" : daily + t("02:00:00") + provide(GPY_JVM21_STANDALONE),
+ "darwin:aarch64:jdk21" : daily + t("01:00:00") + provide(GPY_JVM21_STANDALONE),
+ "windows:amd64:jdk21" : daily + t("01:30:00"),
"linux:amd64:jdk-latest" : tier2 + batches(2),
"linux:aarch64:jdk-latest" : tier3,
"darwin:aarch64:jdk-latest" : tier3,
}),
+ "python-unittest-multi-context-sandboxed": gpgate_ee + platform_spec(no_jobs) + platform_spec({
+ "linux:amd64:jdk21" : daily + t("01:00:00") + require(GPY_JVM21_STANDALONE),
+ "linux:aarch64:jdk21" : daily + t("01:30:00") + require(GPY_JVM21_STANDALONE),
+ "darwin:aarch64:jdk21" : daily + t("01:00:00") + require(GPY_JVM21_STANDALONE),
+ "windows:amd64:jdk21" : daily + t("02:00:00"),
+ }),
"python-svm-unittest-sandboxed": gpgate_ee + platform_spec(no_jobs) + platform_spec({
"linux:amd64:jdk-latest" : tier3 + provide(GPYEE_NATIVE_STANDALONE),
}),
diff --git a/docs/contributor/IMPLEMENTATION_DETAILS.md b/docs/contributor/IMPLEMENTATION_DETAILS.md
index 216a31ee19..b540380319 100644
--- a/docs/contributor/IMPLEMENTATION_DETAILS.md
+++ b/docs/contributor/IMPLEMENTATION_DETAILS.md
@@ -199,24 +199,27 @@ Below is a rough draft of the types and memory layouts involved and how they con
```
Managed Heap Native Heap
Stub allocated to represent managed object
- +------------------------+ +-------------------------------+
- | PInt |<---+ | struct PyGC_Head { |
- +------------------------+ | | uintptr_t _gc_next |
- | BigInteger value | | | uintptr_t _gc_prev |
- +------------------------+ | | } |
- | +------------>| struct GraalPyObject { |
- +------------------------+ | | | Py_ssize_t ob_refcnt |
- | PythonNativeWrapper |<---+ | +------>| PyObject *ob_type |
- +------------------------+------------+ | | int32_t handle_table_index |---+
- | +<---+ | | } | |
- +------------------------+ | | +-------------------------------+ |
- | | |
- | | |
- +------------------------+ | | |
-+---| PythonObjectReference |<---+ | |
-| +------------------------+------------------+ |
-| | boolean gc | |
-| | boolean freeAtCollect | - |
+ +---------------------------+ +-------------------------------+
++-->| PInt | | struct PyGC_Head { |
+| +---------------------------+ | uintptr_t _gc_next |
+| | long nativePointer |---------------+ | uintptr_t _gc_prev |
+| | PythonObjectReference ref |----+ | | } |
+| | BigInteger value | | +------>| struct GraalPyObject { |
+| +---------------------------+ | | | Py_ssize_t ob_refcnt |
+| | | | PyObject *ob_type |
+| | | | int32_t handle_table_index |---+
+| | | | } | |
+| | | +-------------------------------+ |
+| | | |
+| | | |
+| | | |
+| | | |
+| +------------------------+ | | |
++---| PythonObjectReference |<------+ | |
+ +------------------------+ | |
+ | long pointer |------------------+ |
+ | boolean gc | |
++---| boolean freeAtCollect | |
| +------------------------+ |
| |
| +------------------------+ |
@@ -244,7 +247,7 @@ Below is a rough draft of the types and memory layouts involved and how they con
```
-Managed objects are associated with `PythonNativeWrapper` subinstances when
+Managed objects store a pointer to native `GraalPyObject` subinstances when
they go to native, native objects are represented throughout the interpreters
as `PythonAbstractNativeObject`. Both have associated weak references,
`PythonObjectReference` and `NativeObjectReference`, respectively.
@@ -261,14 +264,14 @@ Java code and is, for example, passed as an argument.
When a managed object is passed to a native extension code:
-* We create `PythonNativeWrapper`. We create `PythonNativeWrapper` to
- provide a different interop protocol, and not expose `toNative` and
- `asPointer` on Python objects. The wrapper is stored inside the
+* We allocate a `GraalPyObject`, `GraalPyVarObject`, or `GraalPyFloatObject` in
+ off-heap memory. The native pointer is stored inside the
`PythonAbstractObject`, because pointer identity is relied upon by some
extensions we saw in the wild. (See PythonToNativeNode)
* If the object was a "primitive" (TruffleString, int, long, boolean) we must
- box it first into a `PythonAbstractObject`, so we create a `PString` or
- `PInt` wrapper (or retrieve one for the singletons).
+ first promote the object to an appropriate instance of
+ `PythonAbstractObject` (e.g. `PString` is the promoted object for
+ `TruffleString` and `PInt` for Java `int`, `long`, `boolean`).
* For container types (such as tuples, lists), when their elements are
accessed any primitive elements are (like the previous step) boxed into a
`PythonAbstractObject`.
diff --git a/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiExternalFunctionSignatures.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiExternalFunctionSignatures.java
new file mode 100644
index 0000000000..bc093f6308
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/CApiExternalFunctionSignatures.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface CApiExternalFunctionSignatures {
+}
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/harness.py b/graalpython/com.oracle.graal.python.benchmarks/python/harness.py
index b14edb726d..bb6cfd75b8 100644
--- a/graalpython/com.oracle.graal.python.benchmarks/python/harness.py
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/harness.py
@@ -206,6 +206,22 @@ def warmup(cp_index):
return -1
+def ensure_packages(**package_specs):
+ import sys, os
+
+ rootdir = os.path.dirname(__file__)
+ while os.path.basename(rootdir) != 'graalpython':
+ rootdir = os.path.dirname(rootdir)
+
+ sys.path.append(os.path.join(
+ rootdir,
+ "com.oracle.graal.python.test",
+ "src",
+ ))
+ from tests import ensure_packages
+ ensure_packages(**package_specs)
+
+
def ccompile(name, code):
import sys, os
@@ -287,6 +303,7 @@ def get_bench_module(bench_file):
with _io.FileIO(bench_file, "r") as f:
bench_module.__file__ = bench_file
bench_module.ccompile = ccompile
+ bench_module.ensure_packages = ensure_packages
exec(compile(f.readall(), bench_file, "exec"), bench_module.__dict__)
return bench_module
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-oracledb-load.py b/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-oracledb-load.py
new file mode 100644
index 0000000000..20d0dba0a4
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-oracledb-load.py
@@ -0,0 +1,402 @@
+# Copyright (c) 2025, 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# blog_load.py
+#
+# This is the code used for the blog:
+# https://cjones-oracle.medium.com/direct-path-loads-fast-data-ingestion-with-python-and-oracle-database-c681fb60384f
+#
+# christopher.jones@oracle.com, 2025
+#
+# Compare end-to-end times for reading a CSV file (number, date, string) in
+# chunks and inserting into the DB. Pandas, executemany() and
+# direct_path_load() are compared.
+#
+# The CSV file can be created using blog_create.py found at
+# https://gist.github.com/cjbj/f4b605cb7db9acdf10f4ff3a2ba58d4d, inlined below into the generate_csv function
+#
+# Create a CSV file with 1,000,000 rows:
+# python blog_create.py 1000000
+#
+# Benchmark loading the CSV file in batches of 1,000,000 rows
+# python blog_load.py 1000000
+#
+# Benchmark loading the CSV file in batches of 500,000 rows
+# python blog_load.py 500000
+#
+# Install:
+# python -m pip install oracledb pyarrow sqlalchemy pandas
+# Requires python-oracledb 3.4+
+
+
+ensure_packages(oracledb="3.4.1", pandas="2.2.3", pyarrow="20.0.0", sqlalchemy="2.0.45")
+
+import csv
+from datetime import datetime
+import getpass
+import os
+import sys
+import time
+
+import pyarrow.csv
+from sqlalchemy import create_engine
+import pandas
+
+import oracledb
+
+# startup database with
+# $ podman run --detach --replace --name oracledb -p 1521:1521 -e ORACLE_PWD=graalpy container-registry.oracle.com/database/free:latest
+USERNAME = 'system'
+CONNECTSTRING = 'localhost:1521/freepdb1'
+PASSWORD = "graalpy"
+
+# -----------------------------------------------------------------------------
+
+FILE_NAME = os.path.join(os.path.dirname(__file__), "sample.csv")
+
+if (len(sys.argv) > 1):
+ BATCH_SIZE = int(sys.argv[1])
+else:
+ BATCH_SIZE = 2_000_000
+
+# -----------------------------------------------------------------------------
+
+def createtab(connection, tab):
+ with connection.cursor() as cursor:
+ cursor.execute(f"""drop table if exists {tab} purge""") # 23ai syntax
+ cursor.execute(f"""create table {tab} (
+ id number,
+ dt date,
+ name varchar2(50))""")
+
+# -----------------------------------------------------------------------------
+
+def droptabs(connection, tabs):
+ with connection.cursor() as cursor:
+ for tab in tabs:
+ cursor.execute(f"drop table if exists {tab} purge") # 23ai syntax
+
+# -----------------------------------------------------------------------------
+
+def checkrowcount(connection, tab):
+ r, = connection.cursor().execute(f"select count(*) from {tab}").fetchone()
+ print(f"{r} rows were inserted")
+
+# -----------------------------------------------------------------------------
+
+# Compare tables
+def compare(connection, t1, t2):
+ print(f"\nChecking '{t1}' and '{t2}'")
+
+ sql = f"""(
+ select * from {t1}
+ minus
+ select * from {t2}
+ ) union all (
+ select * from {t2}
+ minus
+ select * from {t1}
+ )"""
+
+ with connection.cursor() as cursor:
+ for r in cursor.execute(sql):
+ print(f"Tables '{t1}' and '{t2}' differ")
+ print(r)
+ exit()
+
+ print(f"Tables are the same")
+
+# -----------------------------------------------------------------------------
+# Using Pandas to read and insert
+
+def pd(tab):
+ print("\nPandas read_csv() - Pandas to_sql()")
+
+ engine = create_engine(
+ "oracle+oracledb://@",
+ connect_args={
+ "user": USERNAME,
+ "password": PASSWORD,
+ "dsn": CONNECTSTRING,
+ },
+ # echo=True
+ )
+
+ start = time.perf_counter_ns()
+
+ csv_reader = pandas.read_csv(
+ FILE_NAME,
+ header=None,
+ names=["id", "dt", "name"],
+ parse_dates=['dt'],
+ chunksize=BATCH_SIZE)
+ for df in csv_reader:
+ df.to_sql(tab, engine, if_exists='append', index=False)
+
+ elapsed = (time.perf_counter_ns() - start) / 1_000_000
+ print(f"Loaded in batches of size {BATCH_SIZE}")
+ print(f"Total elapsed time: {elapsed:,.1f} ms")
+
+# -----------------------------------------------------------------------------
+# Using Python's CSV package to read into a list and then calling executemany()
+
+def em(connection, tab):
+ print("\nPython CSV loader to list - executemany()")
+
+ start = time.perf_counter_ns()
+
+ cursor = connection.cursor()
+
+ cursor.setinputsizes(None, None, 50)
+
+ sql = f"insert into {tab} (id, dt, name) values (:1, :2, :3)"
+ data = []
+ batch_number = 0
+ csv_reader = csv.reader(open(FILE_NAME, "r"), delimiter=",")
+ for line in csv_reader:
+ data.append((float(line[0]), datetime.strptime(line[1], "%d-%b-%Y"), line[2]))
+ if len(data) % BATCH_SIZE == 0:
+ cursor.executemany(sql, data)
+ data = []
+ batch_number += 1
+ if data:
+ cursor.executemany(sql, data)
+ batch_number += 1
+
+ connection.commit()
+
+ elapsed = (time.perf_counter_ns() - start) / 1_000_000
+ print(f"Loaded in {batch_number} batches of size {BATCH_SIZE}")
+ print(f"Total elapsed time: {elapsed:,.1f} ms")
+
+# -----------------------------------------------------------------------------
+# Using PyArrow to read into Dataframe and then calling executemany()
+
+def pyaem(connection, tab):
+ print("\nPyArrow CSV loader to DataFrame - executemany()")
+
+ start = time.perf_counter_ns()
+
+ sql = f"insert into {tab} (id, dt, name) values (:1, :2, :3)"
+ colnames=["id", "dt", "name"]
+
+ read_options = pyarrow.csv.ReadOptions(
+ column_names=colnames,
+ block_size=BLOCK_SIZE
+ )
+
+ convert_options = pyarrow.csv.ConvertOptions(
+ timestamp_parsers=["%d-%b-%Y"],
+ column_types={
+ "id": pyarrow.int64(),
+ "dt": pyarrow.timestamp("us"),
+ "name": pyarrow.string()
+ }
+ )
+
+ cursor = connection.cursor()
+
+ batch_number = 0
+ csv_reader = pyarrow.csv.open_csv(FILE_NAME, read_options=read_options, convert_options=convert_options)
+ for df in csv_reader:
+ if df is None:
+ break
+ batch_number += 1
+ cursor.executemany(sql, df)
+
+ connection.commit()
+
+ elapsed = (time.perf_counter_ns() - start) / 1_000_000
+ print(f"Loaded in {batch_number} batches with block size {BLOCK_SIZE}")
+ print(f"Total elapsed time: {elapsed:,.1f} ms")
+
+# -----------------------------------------------------------------------------
+# Using Python's CSV package to read into a list and then a Direct Path Load
+#
+
+def dpl(connection, tab):
+ print("\nPython CSV loader to list - Direct Path Load")
+
+ start = time.perf_counter_ns()
+
+ column_names = ["id", "dt", "name"]
+ data = []
+ batch_number = 0
+ csv_reader = csv.reader(open(FILE_NAME, "r"), delimiter=",")
+ for line in csv_reader:
+ data.append((float(line[0]), datetime.strptime(line[1], "%d-%b-%Y"), line[2]))
+ if len(data) % BATCH_SIZE == 0:
+ connection.direct_path_load(
+ schema_name=USERNAME,
+ table_name=tab,
+ column_names=column_names,
+ data=data)
+ batch_number += 1
+ data = []
+ if data:
+ connection.direct_path_load(
+ schema_name=USERNAME,
+ table_name=tab,
+ column_names=column_names,
+ data=data)
+ batch_number += 1
+
+ print(f"Loaded in {batch_number} batches of size {BATCH_SIZE}")
+ elapsed = (time.perf_counter_ns() - start) / 1_000_000
+ print(f"Total elapsed time: {elapsed:,.1f} ms")
+
+# -----------------------------------------------------------------------------
+# Using PyArrow to read into Dataframe and then a Direct Path Load
+
+def pya(connection, tab):
+ print("\nPyArrow CSV loader to DataFrame - Direct Path Load ")
+
+ start = time.perf_counter_ns()
+
+ column_names=["id", "dt", "name"]
+
+ read_options = pyarrow.csv.ReadOptions(
+ column_names=column_names,
+ block_size=BLOCK_SIZE
+ )
+
+ convert_options = pyarrow.csv.ConvertOptions(
+ timestamp_parsers=["%d-%b-%Y"],
+ column_types={
+ "id": pyarrow.int64(),
+ "dt": pyarrow.timestamp("us"),
+ "name": pyarrow.string()
+ }
+ )
+
+ csv_reader = pyarrow.csv.open_csv(FILE_NAME, read_options=read_options, convert_options=convert_options)
+ batch_number = 0
+ for df in csv_reader:
+ if df is None:
+ break
+ batch_number += 1
+ connection.direct_path_load(
+ schema_name=USERNAME,
+ table_name=tab,
+ column_names=column_names,
+ data=df)
+
+ elapsed = (time.perf_counter_ns() - start) / 1_000_000
+ print(f"Loaded in {batch_number} batches with block size {BLOCK_SIZE}")
+ print(f"Total elapsed time: {elapsed:,.1f} ms")
+
+# -----------------------------------------------------------------------------
+
+BLOCK_SIZE = 0
+CONNECTION = None
+
+def __setup__(*args):
+ # blog_create.py
+ #
+ # christopher.jones@oracle.com, 2025
+ #
+ # Create a CSV file (number, date, string)
+ import csv
+ import sys
+ from datetime import datetime
+
+
+ num_records = BATCH_SIZE
+ data = [
+ (
+ i + 1,
+ datetime.now().strftime("%d-%b-%Y"),
+ f"String for row {i + 1}"
+ ) for i in range(num_records)
+ ]
+
+ with open(FILE_NAME, "w") as f:
+ writer = csv.writer(
+ f, lineterminator="\n", quoting=csv.QUOTE_NONNUMERIC
+ )
+ writer.writerows(data)
+
+ print(f"Created {FILE_NAME} with {num_records} records")
+
+ global BLOCK_SIZE, CONNECTION
+ BLOCK_SIZE = len(max(open(FILE_NAME, 'r'), key=len)) * BATCH_SIZE
+ CONNECTION = oracledb.connect(user=USERNAME, password=PASSWORD, dsn=CONNECTSTRING)
+
+
+def __benchmark__(num=1):
+ assert num == 1
+ t1 = "mytabpya"
+ createtab(CONNECTION, t1)
+ pya(CONNECTION, t1)
+ checkrowcount(CONNECTION, t1)
+
+ t2 = "mytabdpl"
+ # createtab(CONNECTION, t2)
+ # dpl(CONNECTION, t2)
+ # checkrowcount(CONNECTION, t2)
+
+ t3 = "mytabpyaem"
+ # createtab(CONNECTION, t3)
+ # pyaem(CONNECTION, t3)
+ # checkrowcount(CONNECTION, t3)
+
+ t4 = "mytabem"
+ # createtab(CONNECTION, t4)
+ # em(CONNECTION, t4)
+ # checkrowcount(CONNECTION, t4)
+
+ t5 = "mytabpd"
+ # createtab(CONNECTION, t5)
+ # pd(t5)
+ # checkrowcount(CONNECTION, t5)
+
+ # Check all the tables are the same
+ #compare(CONNECTION, t1, t2); compare(CONNECTION, t2, t3); compare(CONNECTION, t3, t4); compare(CONNECTION, t4, t5)
+
+
+def __cleanup__(*args):
+ droptabs(CONNECTION, [t1, t2, t3, t4, t5])
+
+
+if __name__ == "__main__":
+ __setup__()
+ print("\nCompare end-to-end times for reading a "
+ "CSV file (number, date, string) in chunks and inserting into the Database")
+ __benchmark__(1)
+ __cleanup__()
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-pydantic-validate.py b/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-pydantic-validate.py
new file mode 100644
index 0000000000..2eb03b5f0b
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-pydantic-validate.py
@@ -0,0 +1,57 @@
+# Copyright (c) 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+ensure_packages(pydantic="2.12.5")
+from pydantic import BaseModel, ValidationError
+
+
+class User(BaseModel):
+ id: int
+ name: str
+ email: str
+ active: bool
+
+
+def __benchmark__(iterations=200000):
+ for i in range(iterations):
+ try:
+ u = User(id=i, name=f"User {i}", email=f"user{i}@example.com", active=True)
+ except ValidationError:
+ pass
+ return u
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-pymupdf-parse.py b/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-pymupdf-parse.py
new file mode 100644
index 0000000000..d576159f8f
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/macro/c-pymupdf-parse.py
@@ -0,0 +1,81 @@
+# Copyright (c) 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import io
+import os
+import subprocess
+
+
+def find_cpython3_on_path():
+ exe_name = "python3"
+ paths = os.environ["PATH"].split(os.pathsep)
+ for path in paths:
+ exe_path = os.path.join(path, exe_name)
+ if "cpython" in subprocess.getoutput(f"'{exe_path}' -c 'import sys;print(sys.implementation.name)'"):
+ return exe_path
+
+
+os.environ["CPYTHON_EXE"] = find_cpython3_on_path()
+ensure_packages(pymupdf="1.25.4", pymupdf4llm="0.0.18")
+
+
+import pymupdf
+import pymupdf4llm
+
+PDF_BYTES = None
+
+
+def parse_pdf():
+ pdf_stream = io.BytesIO(PDF_BYTES)
+ docType = "pdf"
+ doc = pymupdf.open(stream=pdf_stream, filetype=docType)
+ # doc = pymupdf.open("./sample.txt")
+ md_text = pymupdf4llm.to_markdown(doc, show_progress = True)
+ doc.close()
+ return md_text
+
+
+def __setup__(*args):
+ global PDF_BYTES
+ with open(os.path.join(os.path.dirname(__file__), "sample.txt"), "rb") as f:
+ PDF_BYTES = f.read()
+
+
+def __benchmark__(num=1):
+ parse_pdf()
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/macro/sample.txt b/graalpython/com.oracle.graal.python.benchmarks/python/macro/sample.txt
new file mode 100644
index 0000000000..e0940624bf
Binary files /dev/null and b/graalpython/com.oracle.graal.python.benchmarks/python/macro/sample.txt differ
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-fastcall.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-fastcall.py
new file mode 100644
index 0000000000..8eb7e8ebfe
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-fastcall.py
@@ -0,0 +1,143 @@
+# Copyright (c) 2026, 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+code = """
+#include "Python.h"
+
+static PyObject* NativeCustomType_method_fastcall(PyObject *module, PyObject *const *args, Py_ssize_t nargs) {
+ Py_ssize_t i;
+ for (i=0; i < nargs; i++) {
+ if (!args[i]) {
+ PyErr_SetString(PyExc_ValueError, "arg must not be NULL");
+ return NULL;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef NativeCustomType_methods[] = {
+ {"method_fastcall", (PyCFunction)NativeCustomType_method_fastcall, METH_FASTCALL, NULL},
+ {NULL, NULL, 0, NULL}
+};
+
+static PyTypeObject NativeCustomType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "NativeCustomType",
+ .tp_basicsize = sizeof(PyObject),
+ .tp_new = PyType_GenericNew,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = NativeCustomType_methods
+};
+
+static PyModuleDef c_method_module = {
+ PyModuleDef_HEAD_INIT,
+ "c_method_module",
+ "",
+ -1,
+ NULL, NULL, NULL, NULL, NULL
+};
+
+PyMODINIT_FUNC
+PyInit_c_method_module(void)
+{
+ PyObject* m;
+
+ if (PyType_Ready(&NativeCustomType) < 0)
+ return NULL;
+
+ m = PyModule_Create(&c_method_module);
+ if (m == NULL)
+ return NULL;
+
+ PyModule_AddObject(m, "NativeCustomType", (PyObject *)&NativeCustomType);
+ return m;
+}
+
+"""
+
+import sys
+import os
+
+# Add benchmark directory to path to allow import of harness.py
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+from harness import ccompile
+
+ccompile("c_method_module", code)
+from c_method_module import NativeCustomType
+
+
+def count(num):
+ obj = NativeCustomType()
+ for i in range(num):
+ res = obj.method_fastcall(i, i+1, i+2)
+ assert res is None
+ return 0
+
+
+def measure(num):
+ return count(num)
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
+
+
+def run():
+ __benchmark__(num=3000)
+
+
+def warmupIterations():
+ return 0
+
+
+def iterations():
+ return 10
+
+
+def summary():
+ return {
+ "name": "OutlierRemovalAverageSummary",
+ "lower-threshold": 0.0,
+ "upper-threshold": 0.7,
+ }
+
+
+def dependencies():
+ # Required to run `ccompile`
+ return ["harness.py", "tests/__init__.py"]
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-noargs.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-noargs.py
new file mode 100644
index 0000000000..1ea2d2547e
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-noargs.py
@@ -0,0 +1,136 @@
+# Copyright (c) 2026, 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+code = """
+#include "Python.h"
+
+static PyObject* NativeCustomType_method_noargs(PyObject* self, PyObject* unused) {
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef NativeCustomType_methods[] = {
+ {"method_noargs", (PyCFunction)NativeCustomType_method_noargs, METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL}
+};
+
+static PyTypeObject NativeCustomType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "NativeCustomType",
+ .tp_basicsize = sizeof(PyObject),
+ .tp_new = PyType_GenericNew,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = NativeCustomType_methods
+};
+
+static PyModuleDef c_method_module = {
+ PyModuleDef_HEAD_INIT,
+ "c_method_module",
+ "",
+ -1,
+ NULL, NULL, NULL, NULL, NULL
+};
+
+PyMODINIT_FUNC
+PyInit_c_method_module(void)
+{
+ PyObject* m;
+
+ if (PyType_Ready(&NativeCustomType) < 0)
+ return NULL;
+
+ m = PyModule_Create(&c_method_module);
+ if (m == NULL)
+ return NULL;
+
+ PyModule_AddObject(m, "NativeCustomType", (PyObject *)&NativeCustomType);
+ return m;
+}
+
+"""
+
+import sys
+import os
+
+# Add benchmark directory to path to allow import of harness.py
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+from harness import ccompile
+
+ccompile("c_method_module", code)
+from c_method_module import NativeCustomType
+
+
+def count(num):
+ obj = NativeCustomType()
+ for _ in range(num):
+ res = obj.method_noargs()
+ assert res is None
+ return 0
+
+
+def measure(num):
+ return count(num)
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
+
+
+def run():
+ __benchmark__(num=3000)
+
+
+def warmupIterations():
+ return 0
+
+
+def iterations():
+ return 10
+
+
+def summary():
+ return {
+ "name": "OutlierRemovalAverageSummary",
+ "lower-threshold": 0.0,
+ "upper-threshold": 0.7,
+ }
+
+
+def dependencies():
+ # Required to run `ccompile`
+ return ["harness.py", "tests/__init__.py"]
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-o.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-o.py
new file mode 100644
index 0000000000..cd12e09123
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-o.py
@@ -0,0 +1,137 @@
+# Copyright (c) 2026, 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+code = """
+#include "Python.h"
+
+static PyObject* NativeCustomType_method_o(PyObject* self, PyObject* arg) {
+ return Py_NewRef(arg);
+}
+
+static struct PyMethodDef NativeCustomType_methods[] = {
+ {"method_o", (PyCFunction)NativeCustomType_method_o, METH_O, NULL},
+ {NULL, NULL, 0, NULL}
+};
+
+static PyTypeObject NativeCustomType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "NativeCustomType",
+ .tp_basicsize = sizeof(PyObject),
+ .tp_new = PyType_GenericNew,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = NativeCustomType_methods
+};
+
+static PyModuleDef c_method_module = {
+ PyModuleDef_HEAD_INIT,
+ "c_method_module",
+ "",
+ -1,
+ NULL, NULL, NULL, NULL, NULL
+};
+
+PyMODINIT_FUNC
+PyInit_c_method_module(void)
+{
+ PyObject* m;
+
+ if (PyType_Ready(&NativeCustomType) < 0)
+ return NULL;
+
+ m = PyModule_Create(&c_method_module);
+ if (m == NULL)
+ return NULL;
+
+ PyModule_AddObject(m, "NativeCustomType", (PyObject *)&NativeCustomType);
+ return m;
+}
+
+"""
+
+import sys
+import os
+
+# Add benchmark directory to path to allow import of harness.py
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+from harness import ccompile
+
+ccompile("c_method_module", code)
+from c_method_module import NativeCustomType
+
+
+def count(num):
+ obj = NativeCustomType()
+ total = 0
+ for i in range(num):
+ total += obj.method_o(i)
+ return total
+
+
+def measure(num):
+ result = count(num)
+ return result
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
+
+
+def run():
+ __benchmark__(num=3000)
+
+
+def warmupIterations():
+ return 0
+
+
+def iterations():
+ return 10
+
+
+def summary():
+ return {
+ "name": "OutlierRemovalAverageSummary",
+ "lower-threshold": 0.0,
+ "upper-threshold": 0.7,
+ }
+
+
+def dependencies():
+ # Required to run `ccompile`
+ return ["harness.py", "tests/__init__.py"]
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-obj-args.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-obj-args.py
new file mode 100644
index 0000000000..5a0a99e1c2
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-obj-args.py
@@ -0,0 +1,138 @@
+# Copyright (c) 2026, 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# The code below calls an API function in a loop to measure performance of C
+# extensions calling into the Python C API. The _PyObject_IsFreed function is
+# such an API function, and it does not do much, for 0 it's just a c-to-c call.
+code = """
+#include "Python.h"
+#include
+
+PyObject* call_method_obj_args(PyObject *module, PyObject *const *args, Py_ssize_t nargs) {
+ if (!_PyArg_CheckPositional("call_method_obj_args", nargs, 3, 3)) {
+ return NULL;
+ }
+ long n = PyLong_AsLong(args[0]);
+ PyObject *receiver = args[1];
+ PyObject *arg = args[2];
+ PyObject *result;
+ for (long i = 0; i < n; i++) {
+ result = PyObject_CallFunctionObjArgs(receiver, arg, NULL);
+ if (result == NULL) {
+ return NULL;
+ }
+ Py_DECREF(result);
+ }
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef MyModuleMethods[] = {
+ {"call_method_obj_args", _PyCFunction_CAST(call_method_obj_args), METH_FASTCALL, NULL},
+ {NULL, NULL, 0, NULL} // Sentinel
+};
+
+static PyModuleDef native_type_module = {
+ PyModuleDef_HEAD_INIT,
+ "native_type_module",
+ NULL,
+ -1,
+ MyModuleMethods
+};
+
+typedef struct {
+ PyObject_HEAD
+ vectorcallfunc vectorcall;
+} NativeObject;
+
+static PyObject *
+NativeType_vectorcall(PyObject *callable, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames)
+{
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+NativeType_new(PyTypeObject* type, PyObject* args, PyObject *kw)
+{
+ NativeObject *op = (NativeObject *)type->tp_alloc(type, 0);
+ op->vectorcall = NativeType_vectorcall;
+ return (PyObject *)op;
+}
+
+static PyTypeObject NativeType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "NativeType",
+ sizeof(NativeObject),
+ .tp_new = NativeType_new,
+ .tp_call = PyVectorcall_Call,
+ .tp_vectorcall_offset = offsetof(NativeObject, vectorcall),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL,
+};
+
+PyMODINIT_FUNC
+PyInit_native_type_module(void)
+{
+ PyType_Ready(&NativeType);
+ PyObject* m = PyModule_Create(&native_type_module);
+ if (!m) {
+ return NULL;
+ }
+ PyModule_AddObject(m, "NativeType", (PyObject *)&NativeType);
+ return m;
+}
+"""
+
+
+ccompile("native_type_module", code)
+from native_type_module import NativeType, call_method_obj_args
+
+def count(num):
+ callable = NativeType()
+ if call_method_obj_args(num, callable, None) is not None:
+ raise RuntimeError()
+ return 0
+
+
+def measure(num):
+ result = count(num)
+ return result
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-varargs.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-varargs.py
new file mode 100644
index 0000000000..1b476cfc49
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-call-method-varargs.py
@@ -0,0 +1,144 @@
+# Copyright (c) 2026, 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+code = """
+#include "Python.h"
+
+static PyObject* NativeCustomType_method_varargs(PyObject* self, PyObject* args) {
+ Py_ssize_t i;
+ Py_ssize_t nargs = PyTuple_GET_SIZE(args);
+ for (i=0; i < nargs; i++) {
+ if (!PyTuple_GET_ITEM(args, i)) {
+ PyErr_SetString(PyExc_ValueError, "arg must not be NULL");
+ return NULL;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef NativeCustomType_methods[] = {
+ {"method_varargs", (PyCFunction)NativeCustomType_method_varargs, METH_VARARGS, NULL},
+ {NULL, NULL, 0, NULL}
+};
+
+static PyTypeObject NativeCustomType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "NativeCustomType",
+ .tp_basicsize = sizeof(PyObject),
+ .tp_new = PyType_GenericNew,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_methods = NativeCustomType_methods
+};
+
+static PyModuleDef c_method_module = {
+ PyModuleDef_HEAD_INIT,
+ "c_method_module",
+ "",
+ -1,
+ NULL, NULL, NULL, NULL, NULL
+};
+
+PyMODINIT_FUNC
+PyInit_c_method_module(void)
+{
+ PyObject* m;
+
+ if (PyType_Ready(&NativeCustomType) < 0)
+ return NULL;
+
+ m = PyModule_Create(&c_method_module);
+ if (m == NULL)
+ return NULL;
+
+ PyModule_AddObject(m, "NativeCustomType", (PyObject *)&NativeCustomType);
+ return m;
+}
+
+"""
+
+import sys
+import os
+
+# Add benchmark directory to path to allow import of harness.py
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+from harness import ccompile
+
+ccompile("c_method_module", code)
+from c_method_module import NativeCustomType
+
+
+def count(num):
+ obj = NativeCustomType()
+ for i in range(num):
+ res = obj.method_varargs(i, i+1, i+2)
+ assert res is None
+ return 0
+
+
+def measure(num):
+ return count(num)
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
+
+
+def run():
+ __benchmark__(num=3000)
+
+
+def warmupIterations():
+ return 0
+
+
+def iterations():
+ return 10
+
+
+def summary():
+ return {
+ "name": "OutlierRemovalAverageSummary",
+ "lower-threshold": 0.0,
+ "upper-threshold": 0.7,
+ }
+
+
+def dependencies():
+ # Required to run `ccompile`
+ return ["harness.py", "tests/__init__.py"]
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-dict-iterating.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-dict-iterating.py
new file mode 100644
index 0000000000..fbb6dbf897
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-dict-iterating.py
@@ -0,0 +1,122 @@
+# Copyright (c) 2026, 2026, 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# The code below calls an API function in a loop to measure performance of C
+# extensions calling into the Python C API. The _PyObject_IsFreed function is
+# such an API function, and it does not do much, for 0 it's just a c-to-c call.
+code = """
+#include "Python.h"
+#include
+
+static PyObject* iterate_dict(PyObject *module, PyObject *const *args, Py_ssize_t nargs) {
+ if (!_PyArg_CheckPositional("iterate_dict", nargs, 2, 2)) {
+ return NULL;
+ }
+ long n = PyLong_AsLong(args[0]);
+ PyObject *arg = args[1];
+
+ /* variables for dict iteration */
+ PyObject *key;
+ PyObject *value;
+ Py_ssize_t pos;
+ Py_hash_t hash;
+
+ if (!PyDict_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError, "arg must be a dict");
+ return NULL;
+ }
+
+ long long cnt = 0;
+ for (long i = 0; i < n; i++) {
+ cnt = 0;
+ pos = 0;
+ while (_PyDict_Next(arg, &pos, &key, &value, &hash)) {
+ if (key == NULL) {
+ PyErr_SetString(PyExc_ValueError, "key must not be NULL");
+ return NULL;
+ }
+ if (value == NULL) {
+ PyErr_SetString(PyExc_ValueError, "value must not be NULL");
+ return NULL;
+ }
+ cnt++;
+ }
+ }
+ return PyLong_FromLongLong(cnt);
+}
+
+static PyMethodDef MyModuleMethods[] = {
+ {"iterate_dict", _PyCFunction_CAST(iterate_dict), METH_FASTCALL, NULL},
+ {NULL, NULL, 0, NULL} // Sentinel
+};
+
+static PyModuleDef iterate_dict_module = {
+ PyModuleDef_HEAD_INIT,
+ "iterate_dict_module",
+ NULL,
+ -1,
+ MyModuleMethods
+};
+
+PyMODINIT_FUNC
+PyInit_iterate_dict_module(void)
+{
+ return PyModule_Create(&iterate_dict_module);
+}
+"""
+
+ccompile("iterate_dict_module", code)
+from iterate_dict_module import iterate_dict
+
+
+def count(num):
+ d = {"a": 1, "b": 2, "c": 3}
+ result = iterate_dict(num, d)
+ if result != len(d):
+ raise RuntimeError("was: " + str(result))
+ return 0
+
+
+def measure(num):
+ result = count(num)
+ return result
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-to-c.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-to-c.py
new file mode 100644
index 0000000000..d95986136c
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-to-c.py
@@ -0,0 +1,90 @@
+# 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# The code below calls an API function in a loop to measure performance of C
+# extensions calling into the Python C API. The _PyObject_IsFreed function is
+# such an API function, and it does not do much, for 0 it's just a c-to-c call.
+code = """
+#include "Python.h"
+
+PyObject* simple_to_c(PyObject* mod, PyObject* arg) {
+ long upper = PyLong_AsLong(arg);
+ for (long i = 0; i < upper; i++) {
+ _PyObject_IsFreed((PyObject *)0L);
+ }
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef MyModuleMethods[] = {
+ {"simple_to_c", simple_to_c, METH_O, NULL},
+ {NULL, NULL, 0, NULL} // Sentinel
+};
+
+static PyModuleDef simple_to_c_module = {
+ PyModuleDef_HEAD_INIT,
+ "simple_to_c_module",
+ NULL,
+ -1,
+ MyModuleMethods
+};
+
+PyMODINIT_FUNC
+PyInit_simple_to_c_module(void)
+{
+ return PyModule_Create(&simple_to_c_module);
+}
+"""
+
+
+ccompile("simple_to_c_module", code)
+from simple_to_c_module import simple_to_c
+
+def count(num):
+ if simple_to_c(num) is not None:
+ raise RuntimeError()
+ return 0
+
+
+def measure(num):
+ result = count(num)
+ return result
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall-builtin-function.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall-builtin-function.py
new file mode 100644
index 0000000000..d3975b3f16
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall-builtin-function.py
@@ -0,0 +1,107 @@
+# 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+code = """
+#include "Python.h"
+
+PyObject* simple_upcall(PyObject* mod, PyObject* args) {
+ PyObject *func = PyTuple_GetItem(args, 0);
+ PyObject *self = PyTuple_GetItem(args, 1);
+ long upper = PyLong_AsLong(PyTuple_GetItem(args, 2));
+ if (!PyCFunction_Check(func)) {
+ PyErr_SetString(PyExc_TypeError, "expected a builtin method");
+ return NULL;
+ }
+#ifdef GRAALVM_PYTHON
+ PyMethodDef *ml = GraalPyCFunction_GetMethodDef(func);
+#else
+ PyCFunctionObject *cfunc = (PyCFunctionObject*)func;
+ PyMethodDef *ml = cfunc->m_ml;
+#endif
+ PyCFunction ml_meth = ml->ml_meth;
+
+ for (long i = 0; i < upper; i++) {
+ PyObject *res = ml_meth(self, NULL);
+ Py_XDECREF(res);
+ if (!res) {
+ return NULL;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef MyModuleMethods[] = {
+ {"simple_upcall", simple_upcall, METH_O, NULL},
+ {NULL, NULL, 0, NULL} // Sentinel
+};
+
+static PyModuleDef simple_upcall_module = {
+ PyModuleDef_HEAD_INIT,
+ "simple_upcall_module",
+ NULL,
+ -1,
+ MyModuleMethods
+};
+
+PyMODINIT_FUNC
+PyInit_simple_upcall_module(void)
+{
+ return PyModule_Create(&simple_upcall_module);
+}
+"""
+
+
+ccompile("simple_upcall_module", code)
+from simple_upcall_module import simple_upcall
+
+def count(num):
+ lst = []
+ my_tuple = (lst.clear, lst, num)
+ if simple_upcall(my_tuple) is not None:
+ raise RuntimeError()
+ return 0
+
+
+def measure(num):
+ result = count(num)
+ return result
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall-slot.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall-slot.py
new file mode 100644
index 0000000000..eb137d4451
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall-slot.py
@@ -0,0 +1,98 @@
+# 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+code = """
+#include "Python.h"
+
+PyObject* simple_upcall(PyObject* mod, PyObject* args) {
+ PyObject *self = PyTuple_GetItem(args, 0);
+ long upper = PyLong_AsLong(PyTuple_GetItem(args, 1));
+ if (!PyList_Check(self)) {
+ PyErr_Format(PyExc_TypeError, "expected list, not '%.200s'", Py_TYPE(self)->tp_name);
+ return NULL;
+ }
+ lenfunc f = PyList_Type.tp_as_sequence->sq_length;
+ for (long i = 0; i < upper; i++) {
+ Py_ssize_t len = f(self);
+ if (len < 0) {
+ return NULL;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef MyModuleMethods[] = {
+ {"simple_upcall", simple_upcall, METH_O, NULL},
+ {NULL, NULL, 0, NULL} // Sentinel
+};
+
+static PyModuleDef simple_upcall_module = {
+ PyModuleDef_HEAD_INIT,
+ "simple_upcall_module",
+ NULL,
+ -1,
+ MyModuleMethods
+};
+
+PyMODINIT_FUNC
+PyInit_simple_upcall_module(void)
+{
+ return PyModule_Create(&simple_upcall_module);
+}
+"""
+
+
+ccompile("simple_upcall_module", code)
+from simple_upcall_module import simple_upcall
+
+def count(num):
+ lst = []
+ my_tuple = (lst, num)
+ if simple_upcall(my_tuple) is not None:
+ raise RuntimeError()
+ return 0
+
+
+def measure(num):
+ result = count(num)
+ return result
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
diff --git a/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall.py b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall.py
new file mode 100644
index 0000000000..f79343a7c3
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.benchmarks/python/micro/c-upcall.py
@@ -0,0 +1,87 @@
+# 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
+#
+# Subject to the condition set forth below, permission is hereby granted to any
+# person obtaining a copy of this software, associated documentation and/or
+# data (collectively the "Software"), free of charge and under any and all
+# copyright rights in the Software, and any and all patent rights owned or
+# freely licensable by each licensor hereunder covering either (i) the
+# unmodified Software as contributed to or provided by such licensor, or (ii)
+# the Larger Works (as defined below), to deal in both
+#
+# (a) the Software, and
+#
+# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+# one is included with the Software each a "Larger Work" to which the Software
+# is contributed by such licensors),
+#
+# without restriction, including without limitation the rights to copy, create
+# derivative works of, display, perform, and distribute the Software and make,
+# use, sell, offer for sale, import, export, have made, and have sold the
+# Software and the Larger Work(s), and to sublicense the foregoing rights on
+# either these or other terms.
+#
+# This license is subject to the following condition:
+#
+# The above copyright notice and either this complete permission notice or at a
+# minimum a reference to the UPL must be included in all copies or substantial
+# portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+code = """
+#include "Python.h"
+
+PyObject* simple_upcall(PyObject* mod, PyObject* arg) {
+ long upper = PyLong_AsLong(arg);
+ for (long i = 0; i < upper; i++) {
+ PyThread_get_thread_ident();
+ }
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef MyModuleMethods[] = {
+ {"simple_upcall", simple_upcall, METH_O, NULL},
+ {NULL, NULL, 0, NULL} // Sentinel
+};
+
+static PyModuleDef simple_upcall_module = {
+ PyModuleDef_HEAD_INIT,
+ "simple_upcall_module",
+ NULL,
+ -1,
+ MyModuleMethods
+};
+
+PyMODINIT_FUNC
+PyInit_simple_upcall_module(void)
+{
+ return PyModule_Create(&simple_upcall_module);
+}
+"""
+
+
+ccompile("simple_upcall_module", code)
+from simple_upcall_module import simple_upcall
+
+def count(num):
+ if simple_upcall(num) is not None:
+ raise RuntimeError()
+ return 0
+
+
+def measure(num):
+ result = count(num)
+ return result
+
+
+def __benchmark__(num=1000000):
+ return measure(num)
diff --git a/graalpython/com.oracle.graal.python.cext/CMakeLists.txt b/graalpython/com.oracle.graal.python.cext/CMakeLists.txt
index 27df0cb568..346c6fcdd4 100644
--- a/graalpython/com.oracle.graal.python.cext/CMakeLists.txt
+++ b/graalpython/com.oracle.graal.python.cext/CMakeLists.txt
@@ -370,6 +370,9 @@ target_compile_definitions(${TARGET_PYEXPAT} PRIVATE
###################### CTYPES ######################
native_module("_ctypes_test" TRUE "${SRC_DIR}/modules/_ctypes/_ctypes_test.c")
+if(WIN32)
+ target_compile_definitions("_ctypes_test" PRIVATE MS_WIN32 MS_WINDOWS)
+endif()
set(CTYPES_SRC
"${SRC_DIR}/modules/_ctypes/_ctypes.c"
"${SRC_DIR}/modules/_ctypes/callbacks.c"
@@ -382,6 +385,9 @@ if(APPLE)
set(CTYPES_SRC ${CTYPES_SRC} "${SRC_DIR}/modules/_ctypes/malloc_closure.c")
endif()
native_module("_ctypes" TRUE "${CTYPES_SRC}")
+if(WIN32)
+ target_compile_definitions("_ctypes" PRIVATE MS_WIN32 MS_WINDOWS)
+endif()
target_include_directories("_ctypes" PUBLIC "${SRC_DIR}/modules/_ctypes")
find_library(FFI_LIBRARY
NAMES "ffi.lib" "libffi.a"
diff --git a/graalpython/com.oracle.graal.python.cext/expat/xmltok_ns.c b/graalpython/com.oracle.graal.python.cext/expat/xmltok_ns.c
index 919c74e9f9..d0310c1904 100644
--- a/graalpython/com.oracle.graal.python.cext/expat/xmltok_ns.c
+++ b/graalpython/com.oracle.graal.python.cext/expat/xmltok_ns.c
@@ -90,6 +90,9 @@ static const ENCODING *
NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) {
# define ENCODING_MAX 128
char buf[ENCODING_MAX];
+#ifndef NDEBUG // GraalPy change: satisfy compiler for debug build
+ memset(buf, '\0', sizeof(buf));
+#endif // GraalPy change
char *p = buf;
int i;
XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
diff --git a/graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h b/graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h
index 5de7d109dc..2b4134cb94 100644
--- a/graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h
+++ b/graalpython/com.oracle.graal.python.cext/include/cpython/pystate.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2020, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2020, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2020 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -105,6 +105,12 @@ typedef struct _err_stackitem {
} _PyErr_StackItem;
+typedef struct {
+ PyObject **items;
+ int len;
+ int capacity;
+} GraalPyDeallocState;
+
typedef struct _stack_chunk {
struct _stack_chunk *previous;
size_t size;
@@ -262,6 +268,9 @@ struct _ts {
/* GraalPy change: We add field 'gc' which corresponds to field
'&interp->gc'. */
struct _gc_runtime_state *gc;
+
+ /* GraalPy change: stack of native objects currently being deallocated. */
+ GraalPyDeallocState graalpy_deallocating;
};
/* WASI has limited call stack. Python's recursion limit depends on code
diff --git a/graalpython/com.oracle.graal.python.cext/include/cpython/tupleobject.h b/graalpython/com.oracle.graal.python.cext/include/cpython/tupleobject.h
index b946ab75c3..0b4546a700 100644
--- a/graalpython/com.oracle.graal.python.cext/include/cpython/tupleobject.h
+++ b/graalpython/com.oracle.graal.python.cext/include/cpython/tupleobject.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2020, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2020, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2020 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -12,8 +12,7 @@ typedef struct {
/* ob_item contains space for 'ob_size' elements.
Items must normally not be NULL, except during construction when
the tuple is not yet visible outside the function that builds it. */
- // Truffle change: PyObject *ob_item[1] doesn't work for us in Sulong
- PyObject **Py_HIDE_IMPL_FIELD(ob_item);
+ PyObject *ob_item[1];
} PyTupleObject;
PyAPI_FUNC(int) _PyTuple_Resize(PyObject **, Py_ssize_t);
diff --git a/graalpython/com.oracle.graal.python.cext/modules/clinic/sha3module.c.h b/graalpython/com.oracle.graal.python.cext/modules/clinic/sha3module.c.h
index 14b520f34a..ff6c4e236f 100644
--- a/graalpython/com.oracle.graal.python.cext/modules/clinic/sha3module.c.h
+++ b/graalpython/com.oracle.graal.python.cext/modules/clinic/sha3module.c.h
@@ -46,7 +46,8 @@ py_sha3_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"", "usedforsecurity", NULL};
- static _PyArg_Parser _parser = {
+ // GraalPy change: was 'static'
+ _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "sha3_224",
.kwtuple = KWTUPLE,
diff --git a/graalpython/com.oracle.graal.python.cext/src/abstract.c b/graalpython/com.oracle.graal.python.cext/src/abstract.c
index 0d8c31f142..6e5e2c08c2 100644
--- a/graalpython/com.oracle.graal.python.cext/src/abstract.c
+++ b/graalpython/com.oracle.graal.python.cext/src/abstract.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2024, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2024, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2024 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -1571,14 +1571,15 @@ PyNumber_AsSsize_t(PyObject *item, PyObject *err)
}
-#if 0 // GraalPy change
PyObject *
PyNumber_Long(PyObject *o)
{
+#if 0 // GraalPy change
PyObject *result;
PyNumberMethods *m;
PyObject *trunc_func;
Py_buffer view;
+#endif // GraalPy change
if (o == NULL) {
return null_error();
@@ -1587,6 +1588,12 @@ PyNumber_Long(PyObject *o)
if (PyLong_CheckExact(o)) {
return Py_NewRef(o);
}
+
+ /* GraalPy change: The above case already handles boxed long values. Hence,
+ those may not reach this point. */
+ assert (!points_to_py_int_handle(o));
+ return GraalPyPrivate_PyNumber_Long(o);
+#if 0 // GraalPy change
m = Py_TYPE(o)->tp_as_number;
if (m && m->nb_int) { /* This should include subclasses of int */
/* Convert using the nb_int slot, which should return something
@@ -1685,6 +1692,7 @@ PyNumber_Long(PyObject *o)
return type_error("int() argument must be a string, a bytes-like object "
"or a real number, not '%.200s'", o);
+#endif // GraalPy change
}
PyObject *
@@ -1698,6 +1706,19 @@ PyNumber_Float(PyObject *o)
return Py_NewRef(o);
}
+ // GraalPy change
+ if (points_to_py_float_handle(o)) {
+ assert(Py_REFCNT(o) == _Py_IMMORTAL_REFCNT);
+ return o;
+ }
+ if (points_to_py_int_handle(o)) {
+ assert(Py_REFCNT(o) == _Py_IMMORTAL_REFCNT);
+ return PyFloat_FromDouble(pointer_to_int64(o));
+ }
+
+ return GraalPyPrivate_PyNumber_Float(o);
+
+#if 0 // GraalPy change
PyNumberMethods *m = Py_TYPE(o)->tp_as_number;
if (m && m->nb_float) { /* This should include subclasses of float */
PyObject *res = m->nb_float(o);
@@ -1745,9 +1766,11 @@ PyNumber_Float(PyObject *o)
return PyFloat_FromDouble(PyFloat_AS_DOUBLE(o));
}
return PyFloat_FromString(o);
+#endif // GraalPy change
}
+#if 0 // GraalPy change
PyObject *
PyNumber_ToBase(PyObject *n, int base)
{
diff --git a/graalpython/com.oracle.graal.python.cext/src/call.c b/graalpython/com.oracle.graal.python.cext/src/call.c
index 5aac823ab5..67af8e400b 100644
--- a/graalpython/com.oracle.graal.python.cext/src/call.c
+++ b/graalpython/com.oracle.graal.python.cext/src/call.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2023, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2023, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2022 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -833,6 +833,7 @@ _PyObject_CallMethodId_SizeT(PyObject *obj, _Py_Identifier *name,
Py_DECREF(callable);
return retval;
}
+#endif // GraalPy change
/* --- Call with "..." arguments ---------------------------------- */
@@ -898,7 +899,59 @@ object_vacall(PyThreadState *tstate, PyObject *base,
}
return result;
}
-#endif // GraalPy change
+
+static PyObject *
+method_vacall(PyObject *receiver, PyObject *method_name, va_list vargs)
+{
+ PyObject *small_stack[_PY_FASTCALL_SMALL_STACK];
+ PyObject **stack;
+ Py_ssize_t nargs;
+ PyObject *result;
+ Py_ssize_t i;
+ va_list countva;
+
+ /* Count the number of arguments */
+ va_copy(countva, vargs);
+ nargs = 0;
+ while (1) {
+ PyObject *arg = va_arg(countva, PyObject *);
+ if (arg == NULL) {
+ break;
+ }
+ nargs++;
+ }
+ va_end(countva);
+
+ /* Copy arguments */
+ if (nargs <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) {
+ stack = small_stack;
+ }
+ else {
+ stack = PyMem_Malloc(nargs * sizeof(stack[0]));
+ if (stack == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ }
+
+ i = 0;
+ for (; i < nargs; ++i) {
+ stack[i] = va_arg(vargs, PyObject *);
+ }
+
+#ifdef Py_STATS
+ if (PyFunction_Check(callable)) {
+ EVAL_CALL_STAT_INC(EVAL_CALL_API);
+ }
+#endif
+ /* Call the function */
+ result = GraalPyPrivate_Object_CallMethodObjArgs(receiver, method_name, stack, nargs);
+
+ if (stack != small_stack) {
+ PyMem_Free(stack);
+ }
+ return result;
+}
PyObject *
@@ -940,12 +993,34 @@ PyObject_VectorcallMethod(PyObject *name, PyObject *const *args,
PyObject *
PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
{
- // GraalPy change: different implementation
+ PyThreadState *tstate = _PyThreadState_GET();
+ if (obj == NULL || name == NULL) {
+ return null_error(tstate);
+ }
+
+ /* GraalPy change: If the receiver is a managed object, we only collect
+ the va_list arguments in native and do everything else in managed. */
+ if (points_to_py_handle_space(obj)) {
+ va_list vargs;
+ va_start(vargs, name);
+ PyObject *result = method_vacall(obj, name, vargs);
+ va_end(vargs);
+ return result;
+ }
+
+ PyObject *callable = NULL;
+ int is_method = _PyObject_GetMethod(obj, name, &callable);
+ if (callable == NULL) {
+ return NULL;
+ }
+ obj = is_method ? obj : NULL;
+
va_list vargs;
va_start(vargs, name);
- // the arguments are given as a variable list followed by NULL
- PyObject *result = GraalPyPrivate_Object_CallMethodObjArgs(obj, name, &vargs);
+ PyObject *result = object_vacall(tstate, obj, callable, vargs);
va_end(vargs);
+
+ Py_DECREF(callable);
return result;
}
@@ -953,12 +1028,39 @@ PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
PyObject *
_PyObject_CallMethodIdObjArgs(PyObject *obj, _Py_Identifier *name, ...)
{
- // GraalPy change: different implementation
+ PyThreadState *tstate = _PyThreadState_GET();
+ if (obj == NULL || name == NULL) {
+ return null_error(tstate);
+ }
+
+ PyObject *oname = _PyUnicode_FromId(name); /* borrowed */
+ if (!oname) {
+ return NULL;
+ }
+
+ /* GraalPy change: If the receiver is a managed object, we only collect
+ the va_list arguments in native and do everything else in managed. */
+ if (points_to_py_handle_space(obj)) {
+ va_list vargs;
+ va_start(vargs, name);
+ PyObject *result = method_vacall(obj, oname, vargs);
+ va_end(vargs);
+ return result;
+ }
+
+ PyObject *callable = NULL;
+ int is_method = _PyObject_GetMethod(obj, oname, &callable);
+ if (callable == NULL) {
+ return NULL;
+ }
+ obj = is_method ? obj : NULL;
+
va_list vargs;
va_start(vargs, name);
- // the arguments are given as a variable list followed by NULL
- PyObject *result = GraalPyPrivate_Object_CallMethodObjArgs(obj, _PyUnicode_FromId(name), &vargs);
+ PyObject *result = object_vacall(tstate, obj, callable, vargs);
va_end(vargs);
+
+ Py_DECREF(callable);
return result;
}
@@ -966,12 +1068,14 @@ _PyObject_CallMethodIdObjArgs(PyObject *obj, _Py_Identifier *name, ...)
PyObject *
PyObject_CallFunctionObjArgs(PyObject *callable, ...)
{
- // GraalPy change: different implementation
+ PyThreadState *tstate = _PyThreadState_GET();
va_list vargs;
+ PyObject *result;
+
va_start(vargs, callable);
- // the arguments are given as a variable list followed by NULL
- PyObject *result = GraalPyPrivate_Object_CallFunctionObjArgs(callable, &vargs);
+ result = object_vacall(tstate, NULL, callable, vargs);
va_end(vargs);
+
return result;
}
diff --git a/graalpython/com.oracle.graal.python.cext/src/capi.c b/graalpython/com.oracle.graal.python.cext/src/capi.c
index 3c8e0c4afd..3c74df3598 100644
--- a/graalpython/com.oracle.graal.python.cext/src/capi.c
+++ b/graalpython/com.oracle.graal.python.cext/src/capi.c
@@ -100,6 +100,44 @@ typedef struct {
// defined in 'unicodeobject.c'
void unicode_dealloc(PyObject *unicode);
+NO_INLINE void
+graalpy_dealloc_stack_grow(PyThreadState *tstate)
+{
+ size_t old_capacity;
+ size_t new_capacity;
+
+ assert(tstate != NULL);
+
+ old_capacity = (size_t)tstate->graalpy_deallocating.capacity;
+ new_capacity = old_capacity > 0 ? old_capacity * 2 : 3;
+ if (new_capacity <= old_capacity || new_capacity > INT_MAX) {
+ Py_FatalError("GraalPy deallocating stack capacity overflow");
+ }
+
+ if (GraalPyPrivate_DeallocStack_Grow(tstate, new_capacity) != 0) {
+ Py_FatalError("out of memory while growing GraalPy deallocating stack");
+ }
+}
+
+void
+graalpy_dealloc_stack_push(PyThreadState *tstate, PyObject *op)
+{
+ assert(tstate != NULL);
+ if (UNLIKELY(tstate->graalpy_deallocating.len >= tstate->graalpy_deallocating.capacity)) {
+ graalpy_dealloc_stack_grow(tstate);
+ }
+ tstate->graalpy_deallocating.items[tstate->graalpy_deallocating.len++] = op;
+}
+
+void
+graalpy_dealloc_stack_pop(PyThreadState *tstate, PyObject *op)
+{
+ assert(tstate != NULL);
+ assert(tstate->graalpy_deallocating.len > 0);
+ assert(tstate->graalpy_deallocating.items[tstate->graalpy_deallocating.len - 1] == op);
+ tstate->graalpy_deallocating.items[--tstate->graalpy_deallocating.len] = NULL;
+}
+
static void object_dealloc(PyObject *self) {
Py_TYPE(self)->tp_free(self);
}
@@ -502,108 +540,6 @@ PyAPI_FUNC(size_t) GraalPyPrivate_GetCurrentRSS() {
}
-#define ReadMember(object, offset, T) ((T*)(((char*)object) + offset))[0]
-
-PyAPI_FUNC(int) GraalPyPrivate_ReadShortMember(void* object, Py_ssize_t offset) {
- return ReadMember(object, offset, short);
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_ReadIntMember(void* object, Py_ssize_t offset) {
- return ReadMember(object, offset, int);
-}
-
-PyAPI_FUNC(long) GraalPyPrivate_ReadLongMember(void* object, Py_ssize_t offset) {
- return ReadMember(object, offset, long);
-}
-
-PyAPI_FUNC(double) GraalPyPrivate_ReadFloatMember(void* object, Py_ssize_t offset) {
- return ReadMember(object, offset, float);
-}
-
-PyAPI_FUNC(double) GraalPyPrivate_ReadDoubleMember(void* object, Py_ssize_t offset) {
- return ReadMember(object, offset, double);
-}
-
-PyAPI_FUNC(void*) GraalPyPrivate_ReadPointerMember(void* object, Py_ssize_t offset) {
- return ReadMember(object, offset, void*);
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_ReadCharMember(void* object, Py_ssize_t offset) {
- return ReadMember(object, offset, char);
-}
-
-#define WriteMember(object, offset, value, T) *(T*)(((char*)object) + offset) = (T)(value)
-
-PyAPI_FUNC(int) GraalPyPrivate_WriteShortMember(void* object, Py_ssize_t offset, short value) {
- WriteMember(object, offset, value, short);
- return 0;
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_WriteIntMember(void* object, Py_ssize_t offset, int value) {
- WriteMember(object, offset, value, int);
- return 0;
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_WriteLongMember(void* object, Py_ssize_t offset, long value) {
- WriteMember(object, offset, value, long);
- return 0;
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_WriteFloatMember(void* object, Py_ssize_t offset, double value) {
- WriteMember(object, offset, value, float);
- return 0;
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_WriteDoubleMember(void* object, Py_ssize_t offset, double value) {
- WriteMember(object, offset, value, double);
- return 0;
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_WriteObjectMember(void* object, Py_ssize_t offset, PyObject* value) {
- /* We first need to decref the old value. */
- PyObject *oldv = ReadMember(object, offset, PyObject*);
- Py_XINCREF(value);
- WriteMember(object, offset, value, PyObject*);
- Py_XDECREF(oldv);
- return 0;
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_WritePointerMember(void* object, Py_ssize_t offset, void* value) {
- WriteMember(object, offset, value, void*);
- return 0;
-}
-
-PyAPI_FUNC(int) GraalPyPrivate_WriteCharMember(void* object, Py_ssize_t offset, char value) {
- WriteMember(object, offset, value, char);
- return 0;
-}
-
-#undef ReadMember
-#undef WriteMember
-
-PyAPI_FUNC(int) GraalPyPrivate_PointerCompare(void* x, void* y, int op) {
- switch (op) {
- case Py_LT:
- return x < y;
- case Py_LE:
- return x <= y;
- case Py_EQ:
- return x == y;
- case Py_NE:
- return x != y;
- case Py_GT:
- return x > y;
- case Py_GE:
- return x >= y;
- default:
- return -1;
- }
-}
-
-PyAPI_FUNC(void*) GraalPyPrivate_PointerAddOffset(void* x, Py_ssize_t y) {
- return (char *)x + y;
-}
-
// Implements the basesisze check in typeobject.c:_PyObject_GetState
PyAPI_FUNC(int) GraalPyPrivate_CheckBasicsizeForGetstate(PyTypeObject* type, int slot_num) {
Py_ssize_t basicsize = PyBaseObject_Type.tp_basicsize;
@@ -622,10 +558,6 @@ PyAPI_FUNC(void) GraalPyPrivate_CheckTypeReady(PyTypeObject* type) {
}
}
-PyAPI_FUNC(void*) GraalPyPrivate_VaArgPointer(va_list* va) {
- return va_arg(*va, void*);
-}
-
PyAPI_FUNC(int) GraalPyPrivate_NoOpClear(PyObject* o) {
return 0;
}
@@ -671,13 +603,9 @@ Py_LOCAL_SYMBOL TruffleContext* TRUFFLE_CONTEXT;
*/
Py_LOCAL_SYMBOL int8_t *_graalpy_finalizing = NULL;
-PyAPI_FUNC(PyThreadState **) initialize_graal_capi(TruffleEnv* env, void **builtin_closures, GCState *gc, PyThreadState *tstate) {
+PyAPI_FUNC(PyThreadState **) initialize_graal_capi(void **builtin_closures, GCState *gc, PyThreadState *tstate) {
clock_t t = clock();
- if (env) {
- TRUFFLE_CONTEXT = (*env)->getTruffleContext(env);
- }
-
_PyGC_InitState(gc);
/*
diff --git a/graalpython/com.oracle.graal.python.cext/src/capi.h b/graalpython/com.oracle.graal.python.cext/src/capi.h
index e20945ced8..151a843299 100644
--- a/graalpython/com.oracle.graal.python.cext/src/capi.h
+++ b/graalpython/com.oracle.graal.python.cext/src/capi.h
@@ -168,6 +168,10 @@ extern THREAD_LOCAL Py_LOCAL_SYMBOL PyThreadState *tstate_current;
extern Py_LOCAL_SYMBOL int8_t *_graalpy_finalizing;
#define graalpy_finalizing (_graalpy_finalizing != NULL && *_graalpy_finalizing)
+void graalpy_dealloc_stack_grow(PyThreadState *tstate);
+void graalpy_dealloc_stack_push(PyThreadState *tstate, PyObject *op);
+void graalpy_dealloc_stack_pop(PyThreadState *tstate, PyObject *op);
+
#if (__linux__ && __GNU_LIBRARY__)
#include
#include
@@ -252,79 +256,6 @@ GraalPyPrivate_Log(int level, const char *format, ...)
Py_LOCAL_SYMBOL int is_builtin_type(PyTypeObject *tp);
-
-#define JWRAPPER_DIRECT 1
-#define JWRAPPER_FASTCALL 2
-#define JWRAPPER_FASTCALL_WITH_KEYWORDS 3
-#define JWRAPPER_KEYWORDS 4
-#define JWRAPPER_VARARGS 5
-#define JWRAPPER_NOARGS 6
-#define JWRAPPER_O 7
-#define JWRAPPER_METHOD 8
-#define JWRAPPER_UNSUPPORTED 9
-#define JWRAPPER_ALLOC 10
-#define JWRAPPER_GETATTR 11
-#define JWRAPPER_SETATTR 12
-#define JWRAPPER_RICHCMP 13
-#define JWRAPPER_SETITEM 14
-#define JWRAPPER_UNARYFUNC 15
-#define JWRAPPER_BINARYFUNC 16
-#define JWRAPPER_BINARYFUNC_L 17
-#define JWRAPPER_BINARYFUNC_R 18
-#define JWRAPPER_TERNARYFUNC 19
-#define JWRAPPER_TERNARYFUNC_R 20
-#define JWRAPPER_LT 21
-#define JWRAPPER_LE 22
-#define JWRAPPER_EQ 23
-#define JWRAPPER_NE 24
-#define JWRAPPER_GT 25
-#define JWRAPPER_GE 26
-#define JWRAPPER_ITERNEXT 27
-#define JWRAPPER_INQUIRY 28
-#define JWRAPPER_DELITEM 29
-#define JWRAPPER_GETITEM 30
-#define JWRAPPER_GETTER 31
-#define JWRAPPER_SETTER 32
-#define JWRAPPER_INITPROC 33
-#define JWRAPPER_HASHFUNC 34
-#define JWRAPPER_CALL 35
-#define JWRAPPER_SETATTRO 36
-#define JWRAPPER_DESCR_GET 37
-#define JWRAPPER_DESCR_SET 38
-#define JWRAPPER_LENFUNC 39
-#define JWRAPPER_OBJOBJPROC 40
-#define JWRAPPER_OBJOBJARGPROC 41
-#define JWRAPPER_NEW 42
-#define JWRAPPER_MP_DELITEM 43
-#define JWRAPPER_STR 44
-#define JWRAPPER_REPR 45
-#define JWRAPPER_DESCR_DELETE 46
-#define JWRAPPER_DELATTRO 47
-#define JWRAPPER_SSIZE_ARG 48
-#define JWRAPPER_VISITPROC 49
-#define JWRAPPER_TRAVERSEPROC 50
-
-
-static inline int get_method_flags_wrapper(int flags) {
- if (flags < 0)
- return JWRAPPER_DIRECT;
- if ((flags & (METH_FASTCALL | METH_KEYWORDS | METH_METHOD)) == (METH_FASTCALL | METH_KEYWORDS | METH_METHOD))
- return JWRAPPER_METHOD;
- if ((flags & (METH_FASTCALL | METH_KEYWORDS)) == (METH_FASTCALL | METH_KEYWORDS))
- return JWRAPPER_FASTCALL_WITH_KEYWORDS;
- if (flags & METH_FASTCALL)
- return JWRAPPER_FASTCALL;
- if (flags & METH_KEYWORDS)
- return JWRAPPER_KEYWORDS;
- if (flags & METH_VARARGS)
- return JWRAPPER_VARARGS;
- if (flags & METH_NOARGS)
- return JWRAPPER_NOARGS;
- if (flags & METH_O)
- return JWRAPPER_O;
- return JWRAPPER_UNSUPPORTED;
-}
-
PyAPI_FUNC(void) GraalPyPrivate_Object_GC_Del(void *op);
// export the SizeT arg parse functions, because we use them in contrast to cpython on windows for core modules that we link dynamically
diff --git a/graalpython/com.oracle.graal.python.cext/src/ceval.c b/graalpython/com.oracle.graal.python.cext/src/ceval.c
index f74e8406d2..0a253f27e6 100644
--- a/graalpython/com.oracle.graal.python.cext/src/ceval.c
+++ b/graalpython/com.oracle.graal.python.cext/src/ceval.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2024, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2024, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2024 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -1790,8 +1790,8 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
Py_DECREF(defaults);
return res;
#else // GraalPy change
- if (globals == NULL) {
- PyErr_SetString(PyExc_SystemError, "PyEval_EvalCodeEx: NULL globals");
+ if (!PyDict_Check(globals)) {
+ PyErr_BadInternalCall();
return NULL;
}
return GraalPyPrivate_Eval_EvalCodeEx(_co, globals, locals != NULL ? locals : Py_None,
diff --git a/graalpython/com.oracle.graal.python.cext/src/descrobject.c b/graalpython/com.oracle.graal.python.cext/src/descrobject.c
index 71d65e86ca..ab87d9513d 100644
--- a/graalpython/com.oracle.graal.python.cext/src/descrobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/descrobject.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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,12 +41,10 @@
#include "capi.h"
PyObject* PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method) {
- int flags = method->ml_flags;
return GraalPyPrivate_Descr_NewClassMethod(method,
method->ml_name,
method->ml_doc,
- flags,
- get_method_flags_wrapper(flags),
+ method->ml_flags,
method->ml_meth,
type);
}
diff --git a/graalpython/com.oracle.graal.python.cext/src/dictobject.c b/graalpython/com.oracle.graal.python.cext/src/dictobject.c
index f98346e5df..1abc5634f3 100644
--- a/graalpython/com.oracle.graal.python.cext/src/dictobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/dictobject.c
@@ -2317,12 +2317,22 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
Py_DECREF(d);
return NULL;
}
+#endif // GraalPy change
/* Methods */
static void
dict_dealloc(PyDictObject *mp)
{
+ /* Special case for native dict subclasses: we need to
+ * prevent that the native part is free'd twice because
+ * the managed object still refers to the native part.
+ */
+ if (!points_to_py_handle_space(mp)) {
+ GraalPyPrivate_Dict_UnlinkNativePart((PyObject *)mp);
+ Py_TYPE(mp)->tp_free((PyObject *)mp);
+ }
+#if 0 // GraalPy change
PyInterpreterState *interp = _PyInterpreterState_GET();
assert(Py_REFCNT(mp) == 0);
Py_SET_REFCNT(mp, 1);
@@ -2366,9 +2376,11 @@ dict_dealloc(PyDictObject *mp)
Py_TYPE(mp)->tp_free((PyObject *)mp);
}
Py_TRASHCAN_END
+#endif // GraalPy change
}
+#if 0 // GraalPy change
static PyObject *
dict_repr(PyDictObject *mp)
{
@@ -3836,7 +3848,7 @@ PyTypeObject PyDict_Type = {
"dict",
sizeof(PyDictObject),
0,
- 0, /* tp_dealloc */ // GraalPy change: nulled
+ (destructor)dict_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
diff --git a/graalpython/com.oracle.graal.python.cext/src/gcmodule.c b/graalpython/com.oracle.graal.python.cext/src/gcmodule.c
index c44369e9aa..f354d504ed 100644
--- a/graalpython/com.oracle.graal.python.cext/src/gcmodule.c
+++ b/graalpython/com.oracle.graal.python.cext/src/gcmodule.c
@@ -1348,6 +1348,7 @@ handle_legacy_finalizers(PyThreadState *tstate,
GCState *gcstate,
PyGC_Head *finalizers, PyGC_Head *old)
{
+#if 0 // GraalPy change: uncollectable objects are not supported
assert(!_PyErr_Occurred(tstate));
// GraalPy change: we do not use this field
// assert(gcstate->garbage != NULL);
@@ -1363,6 +1364,7 @@ handle_legacy_finalizers(PyThreadState *tstate,
}
}
}
+#endif // GraalPy change
gc_list_merge(finalizers, old);
}
@@ -1421,10 +1423,12 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate,
"refcount is too small");
if (gcstate->debug & DEBUG_SAVEALL) {
+#if 0 // GraalPy change: uncollectable objects are not supported
assert(gcstate->garbage != NULL);
if (PyList_Append(gcstate->garbage, op) < 0) {
_PyErr_Clear(tstate);
}
+#endif // GraalPy change
}
else {
inquiry clear;
@@ -1640,15 +1644,16 @@ gc_collect_main(PyThreadState *tstate, int generation,
// _PyTime_t t1 = 0; /* initialize to prevent a compiler warning */
GCState *gcstate = graalpy_get_gc_state(tstate); // GraalPy change
- if (GraalPyPrivate_DisableReferneceQueuePolling()) {
+ if (GraalPyPrivate_DisableReferenceQueuePolling()) {
// reference queue polling is currently active; cannot proceed
return m + n;
}
+#if 0 // GraalPy change: uncollectable objects are not supported
// gc_collect_main() must not be called before _PyGC_Init
// or after _PyGC_Fini()
- // GraalPy change: we are not using the gcstate->garbage field
- // assert(gcstate->garbage != NULL);
+ assert(gcstate->garbage != NULL);
+#endif // GraalPy change
assert(!_PyErr_Occurred(tstate));
if (gcstate->debug & DEBUG_STATS) {
@@ -1798,7 +1803,7 @@ gc_collect_main(PyThreadState *tstate, int generation,
PyDTrace_GC_DONE(n + m);
}
- GraalPyPrivate_EnableReferneceQueuePolling();
+ GraalPyPrivate_EnableReferenceQueuePolling();
assert(!_PyErr_Occurred(tstate));
return n + m;
diff --git a/graalpython/com.oracle.graal.python.cext/src/memoryobject.c b/graalpython/com.oracle.graal.python.cext/src/memoryobject.c
index 296b6e4102..9cc0cc0d85 100644
--- a/graalpython/com.oracle.graal.python.cext/src/memoryobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/memoryobject.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2018, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2020 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -3407,12 +3407,6 @@ PyTypeObject PyMemoryView_Type = {
#endif // GraalPy change
// GraalPy additions
-/* called from memoryview implementation to do pointer arithmetics currently not possible from Java */
-PyAPI_FUNC(int8_t *)
-GraalPyPrivate_AddSuboffset(int8_t *ptr, Py_ssize_t offset, Py_ssize_t suboffset)
-{
- return *(int8_t**)(ptr + offset) + suboffset;
-}
PyAPI_FUNC(PyObject *)
GraalPyPrivate_MemoryViewFromObject(PyObject *v, int flags)
diff --git a/graalpython/com.oracle.graal.python.cext/src/methodobject.c b/graalpython/com.oracle.graal.python.cext/src/methodobject.c
index d8c6edc030..14a4e14c24 100644
--- a/graalpython/com.oracle.graal.python.cext/src/methodobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/methodobject.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -45,8 +45,6 @@
/* undefine macro trampoline to PyCMethod_New */
#undef PyCFunction_NewEx
-typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
-
PyObject *PyCFunction_New(PyMethodDef *ml, PyObject *self) {
return PyCFunction_NewEx(ml, self, NULL);
}
@@ -56,42 +54,49 @@ PyObject *PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) {
}
-PyObject* PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *cls) {
- return GraalPyPrivate_CMethod_NewEx(ml, ml->ml_name,
- ml->ml_meth,
- ml->ml_flags,
- get_method_flags_wrapper(ml->ml_flags),
- self,
- module,
- cls,
- ml->ml_doc);
+PyObject *PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *cls) {
+ // GraalPy change: different implementation
+ int is_method = ml->ml_flags & METH_METHOD;
+ if (is_method && !cls) {
+ PyErr_SetString(PyExc_SystemError,
+ "attempting to create PyCMethod with a METH_METHOD "
+ "flag but no class");
+ return NULL;
+ }
+ if (!is_method && cls) {
+ PyErr_SetString(PyExc_SystemError,
+ "attempting to create PyCFunction with class "
+ "but no METH_METHOD flag");
+ return NULL;
+ }
+ return GraalPyPrivate_CMethod_NewEx(ml, ml->ml_name, ml->ml_meth, ml->ml_flags, self, module, cls, ml->ml_doc);
}
PyCFunction PyCFunction_GetFunction(PyObject *func) {
- PyMethodDef* def = GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
+ PyMethodDef *def = GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
return def->ml_meth;
}
-PyObject * PyCFunction_GetSelf(PyObject *func) {
- PyMethodDef* def = GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
+PyObject *PyCFunction_GetSelf(PyObject *func) {
+ PyMethodDef *def = GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
return def->ml_flags & METH_STATIC ? NULL : GraalPyPrivate_GET_PyCFunctionObject_m_self(func);
}
int PyCFunction_GetFlags(PyObject *func) {
- PyMethodDef* def = GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
+ PyMethodDef *def = GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
return def->ml_flags;
}
-PyTypeObject * GraalPyCMethod_GetClass(PyObject *func) {
- PyMethodDef* def = GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
+PyTypeObject *GraalPyCMethod_GetClass(PyObject *func) {
+ PyMethodDef *def = GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
return def->ml_flags & METH_METHOD ? GraalPyPrivate_GET_PyCMethodObject_mm_class(func) : NULL;
}
-PyObject* GraalPyCFunction_GetModule(PyObject *func) {
+PyObject *GraalPyCFunction_GetModule(PyObject *func) {
return GraalPyPrivate_GET_PyCFunctionObject_m_module(func);
}
-PyMethodDef* GraalPyCFunction_GetMethodDef(PyObject *func) {
+PyMethodDef *GraalPyCFunction_GetMethodDef(PyObject *func) {
return GraalPyPrivate_GET_PyCFunctionObject_m_ml(func);
}
diff --git a/graalpython/com.oracle.graal.python.cext/src/moduleobject.c b/graalpython/com.oracle.graal.python.cext/src/moduleobject.c
index c7b8644dd2..5ddb615d9f 100644
--- a/graalpython/com.oracle.graal.python.cext/src/moduleobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/moduleobject.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2022, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2022, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2022 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -167,10 +167,11 @@ check_api_version(const char *name, int module_api_version)
return 1;
}
-#if 0 // GraalPy change
static int
_add_methods_to_object(PyObject *module, PyObject *name, PyMethodDef *functions)
{
+ return GraalPyPrivate_AddMethodsToObject(module, name, functions);
+#if 0 // GraalPy change
PyObject *func;
PyMethodDef *fdef;
@@ -194,8 +195,8 @@ _add_methods_to_object(PyObject *module, PyObject *name, PyMethodDef *functions)
}
return 0;
-}
#endif // GraalPy change
+}
PyObject *
PyModule_Create2(PyModuleDef* module, int module_api_version)
@@ -408,16 +409,10 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
}
if (def->m_methods != NULL) {
- // GraalPy change: use PyModule_AddFunctions instead of _add_methods_to_object
- if (PyModule_AddFunctions(m, def->m_methods) != 0) {
- Py_DECREF(m);
- return NULL;
+ ret = _add_methods_to_object(m, nameobj, def->m_methods);
+ if (ret != 0) {
+ goto error;
}
- // End of GraalPy change, original code below
- // ret = _add_methods_to_object(m, nameobj, def->m_methods);
- // if (ret != 0) {
- // goto error;
- // }
}
if (def->m_doc != NULL) {
@@ -519,19 +514,11 @@ int
PyModule_AddFunctions(PyObject *m, PyMethodDef *functions)
{
// GraalPy change: different implementation
- if (!functions) {
+ if (!PyModule_Check(m)) {
+ PyErr_BadArgument();
return -1;
}
- for (PyMethodDef* def = functions; def->ml_name != NULL; def++) {
- GraalPyPrivate_Module_AddFunctionToModule(def,
- m,
- def->ml_name,
- def->ml_meth,
- def->ml_flags,
- get_method_flags_wrapper(def->ml_flags),
- def->ml_doc);
- }
- return 0;
+ return GraalPyPrivate_Module_AddFunctions(m, functions);
}
#if 0 // GraalPy change
diff --git a/graalpython/com.oracle.graal.python.cext/src/object.c b/graalpython/com.oracle.graal.python.cext/src/object.c
index b02003c7db..f8c6cadb2e 100644
--- a/graalpython/com.oracle.graal.python.cext/src/object.c
+++ b/graalpython/com.oracle.graal.python.cext/src/object.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2018, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2022 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -2741,14 +2741,13 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg,
Py_FatalError("_PyObject_AssertFailed");
}
-
void
_Py_Dealloc(PyObject *op)
{
+ PyThreadState *tstate = _PyThreadState_GET();
PyTypeObject *type = Py_TYPE(op);
destructor dealloc = type->tp_dealloc;
#ifdef Py_DEBUG
- PyThreadState *tstate = _PyThreadState_GET();
PyObject *old_exc = tstate != NULL ? tstate->current_exception : NULL;
// Keep the old exception type alive to prevent undefined behavior
// on (tstate->curexc_type != old_exc_type) below
@@ -2760,7 +2759,13 @@ _Py_Dealloc(PyObject *op)
#ifdef Py_TRACE_REFS
_Py_ForgetReference(op);
#endif
+ if (tstate != NULL) {
+ graalpy_dealloc_stack_push(tstate, op);
+ }
(*dealloc)(op);
+ if (tstate != NULL) {
+ graalpy_dealloc_stack_pop(tstate, op);
+ }
#ifdef Py_DEBUG
// gh-89373: The tp_dealloc function must leave the current exception
@@ -2855,7 +2860,7 @@ Py_ssize_t Py_REFCNT(PyObject *obj) {
}
res = pointer_to_stub(obj)->ob_refcnt;
#ifndef NDEBUG
- if (GraalPyPrivate_Debug_CAPI() && GraalPyPrivate_GET_PyObject_ob_refcnt(obj) != res)
+ if (GraalPyPrivate_Debug_CAPI() && GraalPyPrivate_Get_PyObject_ob_refcnt(obj) != res)
{
Py_FatalError("Refcount of native stub and managed object differ");
}
@@ -2998,8 +3003,7 @@ _decref_notify(const PyObject *op, const Py_ssize_t updated_refcnt)
GraalPyPrivate_BulkNotifyRefCount(deferred_notify_ops, DEFERRED_NOTIFY_SIZE);
}
#else
- PyObject *nonConstOp = (PyObject *)op;
- GraalPyPrivate_BulkNotifyRefCount(&nonConstOp, 1);
+ GraalPyPrivate_NotifyRefCount((PyObject *) op, updated_refcnt);
#endif
}
}
diff --git a/graalpython/com.oracle.graal.python.cext/src/sysmodule.c b/graalpython/com.oracle.graal.python.cext/src/sysmodule.c
index 3796e7fb4d..79b28f3aaa 100644
--- a/graalpython/com.oracle.graal.python.cext/src/sysmodule.c
+++ b/graalpython/com.oracle.graal.python.cext/src/sysmodule.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -40,6 +40,9 @@
*/
#include "capi.h"
+#include "pycore_pyerrors.h" // _PyErr_GetRaisedException()
+#include "pycore_pystate.h" // _PyThreadState_GET()
+
int PySys_Audit(const char *event, const char *argFormat, ...) {
// ignore for now
return 0;
@@ -84,6 +87,35 @@ PySys_WriteStderr(const char *format, ...)
va_end(va);
}
+static void
+sys_format(int key, FILE *fp, const char *format, va_list va)
+{
+ PyObject *message; // GraalPy change
+ const char *utf8;
+ PyThreadState *tstate = _PyThreadState_GET();
+
+ PyObject *exc = _PyErr_GetRaisedException(tstate);
+ message = PyUnicode_FromFormatV(format, va);
+ if (message != NULL) {
+#if 0 // GraalPy change
+ file = _PySys_GetRequiredAttr(key);
+ if (sys_pyfile_write_unicode(message, file) != 0) {
+#endif // GraalPy change
+ // GraalPy change: different implementation
+ if (GraalPyPrivate_Sys_PyFileWriteUnicode(key, message) != 0) {
+ _PyErr_Clear(tstate);
+ utf8 = PyUnicode_AsUTF8(message);
+ if (utf8 != NULL)
+ fputs(utf8, fp);
+ }
+#if 0 // GraalPy change
+ Py_XDECREF(file);
+#endif // GraalPy change
+ Py_DECREF(message);
+ }
+ _PyErr_SetRaisedException(tstate, exc);
+}
+
void
PySys_FormatStdout(const char *format, ...)
{
@@ -91,7 +123,7 @@ PySys_FormatStdout(const char *format, ...)
va_start(va, format);
// GraalPy change: different implementation
- GraalPyPrivate_Sys_FormatStd(0, format, &va);
+ sys_format(0, stdout, format, va);
va_end(va);
}
@@ -102,6 +134,6 @@ PySys_FormatStderr(const char *format, ...)
va_start(va, format);
// GraalPy change: different implementation
- GraalPyPrivate_Sys_FormatStd(1, format, &va);
+ sys_format(1, stderr, format, va);
va_end(va);
}
diff --git a/graalpython/com.oracle.graal.python.cext/src/tupleobject.c b/graalpython/com.oracle.graal.python.cext/src/tupleobject.c
index 25f9f781b2..354b01eaac 100644
--- a/graalpython/com.oracle.graal.python.cext/src/tupleobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/tupleobject.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2018, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2022 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -17,9 +17,6 @@
#endif // GraalPy change
#include "pycore_object.h" // _PyObject_GC_TRACK(), _Py_FatalRefcountError()
-// GraalPy change
-void GraalPyPrivate_Tuple_Dealloc(PyTupleObject* self);
-
#if 0 // GraalPy change
/*[clinic input]
class tuple "PyTupleObject *" "&PyTuple_Type"
@@ -203,13 +200,13 @@ PyTuple_Pack(Py_ssize_t n, ...)
return result;
}
-#if 0 // GraalPy change
/* Methods */
static void
tupledealloc(PyTupleObject *op)
{
+#if 0 // GraalPy change
if (Py_SIZE(op) == 0) {
/* The empty tuple is statically allocated. */
if (op == &_Py_SINGLETON(tuple_empty)) {
@@ -224,6 +221,7 @@ tupledealloc(PyTupleObject *op)
assert(!PyTuple_CheckExact(op));
#endif
}
+#endif // GraalPy change
PyObject_GC_UnTrack(op);
Py_TRASHCAN_BEGIN(op, tupledealloc)
@@ -233,13 +231,17 @@ tupledealloc(PyTupleObject *op)
Py_XDECREF(op->ob_item[i]);
}
// This will abort on the empty singleton (if there is one).
+#if 0 // GraalPy change
if (!maybe_freelist_push(op)) {
Py_TYPE(op)->tp_free((PyObject *)op);
}
+#endif // GraalPy change
+ Py_TYPE(op)->tp_free((PyObject *)op);
Py_TRASHCAN_END
}
+#if 0 // GraalPy change
static PyObject *
tuplerepr(PyTupleObject *v)
{
@@ -711,6 +713,7 @@ tuplerichcompare(PyObject *v, PyObject *w, int op)
/* Compare the final item again using the proper operator */
return PyObject_RichCompare(vt->ob_item[i], wt->ob_item[i], op);
}
+#endif // GraalPy change
static PyObject *
tuple_subtype_new(PyTypeObject *type, PyObject *iterable);
@@ -737,13 +740,43 @@ tuple_new_impl(PyTypeObject *type, PyObject *iterable)
return tuple_subtype_new(type, iterable);
if (iterable == NULL) {
+#if 0 // GraalPy change
return tuple_get_empty();
+#endif // GraalPy change
+ return PyTuple_New(0);
}
else {
return PySequence_Tuple(iterable);
}
}
+// GraalPy change: imported from 'clinit/tupleobject.c.h'
+static PyObject *
+tuple_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *return_value = NULL;
+ PyTypeObject *base_tp = &PyTuple_Type;
+ PyObject *iterable = NULL;
+
+ if ((type == base_tp || type->tp_init == base_tp->tp_init) &&
+ !_PyArg_NoKeywords("tuple", kwargs)) {
+ goto exit;
+ }
+ if (!_PyArg_CheckPositional("tuple", PyTuple_GET_SIZE(args), 0, 1)) {
+ goto exit;
+ }
+ if (PyTuple_GET_SIZE(args) < 1) {
+ goto skip_optional;
+ }
+ iterable = PyTuple_GET_ITEM(args, 0);
+ skip_optional:
+ return_value = tuple_new_impl(type, iterable);
+
+ exit:
+ return return_value;
+}
+
+#if 0 // GraalPy change
static PyObject *
tuple_vectorcall(PyObject *type, PyObject * const*args,
size_t nargsf, PyObject *kwnames)
@@ -767,37 +800,49 @@ tuple_vectorcall(PyObject *type, PyObject * const*args,
#endif // GraalPy change
-// GraalPy change
-PyObject* GraalPyPrivate_Tuple_Alloc(PyTypeObject* cls, Py_ssize_t nitems);
-
-PyAPI_FUNC(PyObject *) // GraalPy change: export for downcall, rename
-GraalPyPrivate_Tuple_SubtypeNew(PyTypeObject *type, PyObject *iterable)
+static PyObject *
+tuple_subtype_new(PyTypeObject *type, PyObject *iterable)
{
- // GraalPy change: different implementation
PyObject *tmp, *newobj, *item;
Py_ssize_t i, n;
assert(PyType_IsSubtype(type, &PyTuple_Type));
- tmp = iterable == NULL ? PyTuple_New(0) : PySequence_Tuple(iterable);
+ // tuple subclasses must implement the GC protocol
+ assert(_PyType_IS_GC(type));
+
+ tmp = tuple_new_impl(&PyTuple_Type, iterable);
if (tmp == NULL)
return NULL;
assert(PyTuple_Check(tmp));
- n = PyTuple_GET_SIZE(tmp);
-
- /* GraalPy note: we cannot call type->tp_alloc here because managed subtypes don't inherit tp_alloc but get a generic one.
- * In CPython tuple uses the generic one to begin with, so they don't have this problem
- */
- newobj = GraalPyPrivate_Tuple_Alloc(type, n);
+ /* This may allocate an empty tuple that is not the global one. */
+ newobj = type->tp_alloc(type, n = PyTuple_GET_SIZE(tmp));
if (newobj == NULL) {
+ Py_DECREF(tmp);
return NULL;
}
+ // GraalPy change
+ PyObject **src_arr = _PyTuple_ITEMS(tmp);
+ PyObject **arr = _PyTuple_ITEMS(newobj);
for (i = 0; i < n; i++) {
+#if 0 // GraalPy change
item = PyTuple_GET_ITEM(tmp, i);
- Py_INCREF(item);
- ((PyTupleObject*) newobj)->ob_item[i] = Py_NewRef(item); // PyTuple_SETITEM
+ PyTuple_SET_ITEM(newobj, i, Py_NewRef(item));
+#endif // GraalPy change
+ arr[i] = Py_NewRef(src_arr[i]);
}
Py_DECREF(tmp);
- return (PyObject*) newobj;
+
+ // Don't track if a subclass tp_alloc is PyType_GenericAlloc()
+ if (!_PyObject_GC_IS_TRACKED(newobj)) {
+ _PyObject_GC_TRACK(newobj);
+ }
+ return newobj;
+}
+
+PyAPI_FUNC(PyObject *) // GraalPy change: export for downcall, rename
+GraalPyPrivate_Tuple_SubtypeNew(PyTypeObject *type, PyObject *iterable)
+{
+ return tuple_subtype_new(type, iterable);
}
#if 0 // GraalPy change
@@ -900,7 +945,7 @@ PyTypeObject PyTuple_Type = {
"tuple",
sizeof(PyTupleObject) - sizeof(PyObject *),
sizeof(PyObject *),
- (destructor)GraalPyPrivate_Tuple_Dealloc, /* tp_dealloc */ // GraalPy change: different function
+ (destructor)tupledealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
@@ -934,9 +979,9 @@ PyTypeObject PyTuple_Type = {
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
- GraalPyPrivate_Tuple_Alloc, /* tp_alloc */ // GraalPy change
- 0, /* tp_new */ // GraalPy change: nulled
- GraalPyPrivate_Object_GC_Del, /* tp_free */ // GraalPy change: different function
+ 0, /* tp_alloc */
+ tuple_new, /* tp_new */
+ PyObject_GC_Del, /* tp_free */
#if 0 // GraalPy change
.tp_vectorcall = tuple_vectorcall,
#endif // GraalPy change
@@ -1305,74 +1350,6 @@ _PyTuple_DebugMallocStats(FILE *out)
#undef FREELIST_FINALIZED
#endif // GraalPy change
-// GraalPy additions
-PyObject* GraalPyPrivate_Tuple_Alloc(PyTypeObject* type, Py_ssize_t nitems) {
- /*
- * TODO(fa): For 'PyVarObjects' (i.e. 'nitems > 0') we increase the size by 'sizeof(void *)'
- * because this additional pointer can then be used as pointer to the element array.
- * CPython usually embeds the array in the struct but Sulong doesn't currently support that.
- * So we allocate space for the additional array pointer.
- * Also consider any 'PyVarObject' (in particular 'PyTupleObject') if this is fixed.
- *
- * This function is mostly an inlined copy-paste of PyType_GenericAlloc, with different size
- * and added initialization of ob_item
- */
- PyObject *obj;
- const size_t size = _PyObject_VAR_SIZE(type, nitems+1) + sizeof(PyObject **);
- /* note that we need to add one, for the sentinel */
-
- const size_t presize = _PyType_PreHeaderSize(type);
- char *alloc = PyObject_Malloc(size + presize);
- if (alloc == NULL) {
- return PyErr_NoMemory();
- }
-
- // GraalPy change: zero whole object
- memset(alloc, '\0', size + presize);
-
- obj = (PyObject *)(alloc + presize);
- if (presize) {
- // GraalPy change: different header layout
- // ((PyObject **)alloc)[0] = NULL;
- // ((PyObject **)alloc)[1] = NULL;
- _PyObject_GC_Link(obj);
- }
- // GraalPy change: whole memory is zero'd above
- // memset(obj, '\0', size);
-
- if (type->tp_itemsize == 0) {
- _PyObject_Init(obj, type);
- }
- else {
- _PyObject_InitVar((PyVarObject *)obj, type, nitems);
- }
-
- if (_PyType_IS_GC(type)) {
- _PyObject_GC_TRACK(obj);
- }
-
- ((PyTupleObject*)obj)->ob_item = (PyObject **) ((char *)obj + offsetof(PyTupleObject, ob_item) + sizeof(PyObject **));
-
- return obj;
-}
-
-void GraalPyPrivate_Tuple_Dealloc(PyTupleObject* self) {
- PyObject_GC_UnTrack(self);
- if (points_to_py_handle_space(self)) {
- return;
- }
- Py_TRASHCAN_BEGIN(self, GraalPyPrivate_Tuple_Dealloc)
- Py_ssize_t len = PyTuple_GET_SIZE(self);
- if (len > 0) {
- Py_ssize_t i = len;
- while (--i >= 0) {
- Py_XDECREF(self->ob_item[i]);
- }
- }
- Py_TYPE(self)->tp_free((PyObject *)self);
- Py_TRASHCAN_END
-}
-
PyObject **
GraalPyTuple_ITEMS(PyObject *op)
{
diff --git a/graalpython/com.oracle.graal.python.cext/src/typeobject.c b/graalpython/com.oracle.graal.python.cext/src/typeobject.c
index 4b035ece1c..689a30ef4c 100644
--- a/graalpython/com.oracle.graal.python.cext/src/typeobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/typeobject.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, 2025, Oracle and/or its affiliates.
+/* Copyright (c) 2018, 2026, Oracle and/or its affiliates.
* Copyright (C) 1996-2022 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -5038,20 +5038,28 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
extern void
_PyDictKeys_DecRef(PyDictKeysObject *keys);
+#endif // GraalPy change
static void
type_dealloc_common(PyTypeObject *type)
{
+#if 0 // GraalPy change
PyObject *bases = lookup_tp_bases(type);
if (bases != NULL) {
PyObject *exc = PyErr_GetRaisedException();
remove_all_subclasses(type, bases);
PyErr_SetRaisedException(exc);
}
+#endif // GraalPy change
+
+ // GraalPy change
+ GraalPyPrivate_Type_NotifyDealloc(type, type->tp_version_tag);
+ type->tp_version_tag = 0;
}
+#if 0 // GraalPy change
static void
clear_static_tp_subclasses(PyTypeObject *type)
{
@@ -5138,9 +5146,7 @@ type_dealloc(PyTypeObject *type)
_PyObject_GC_UNTRACK(type);
-#if 0 // GraalPy change
type_dealloc_common(type);
-#endif // GraalPy change
// PyObject_ClearWeakRefs() raises an exception if Py_REFCNT() != 0
assert(Py_REFCNT(type) == 0);
@@ -6712,7 +6718,6 @@ type_add_method(PyTypeObject *type, PyMethodDef *meth)
meth->ml_name,
meth->ml_meth,
meth->ml_flags,
- get_method_flags_wrapper(meth->ml_flags),
meth->ml_doc);
}
@@ -7582,6 +7587,10 @@ type_ready(PyTypeObject *type, int rerunbuiltin)
#endif
#endif // GraalPy change
+ /* GraalPy change: We use 'tp_version_tag' to store an index for a fast lookup table. To avoid accidentally
+ incorrect associations, we clear the field in the very beginning. */
+ type->tp_version_tag = 0;
+
/* GraalPy change: IMPORTANT: This is a Truffle-specific statement. Since the refcnt for the type is currently 0 and
we will create several references to this object that will be collected during the execution of
this method, we need to keep it alive. */
@@ -7650,6 +7659,9 @@ type_ready(PyTypeObject *type, int rerunbuiltin)
assert(_PyType_CheckConsistency(type));
+ // GraalPy change
+ GraalPyPrivate_NotifyTypeReady(type);
+
// GraalPy change: for reason, see first call to Py_INCREF in this function
Py_DECREF(type);
return 0;
diff --git a/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c b/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c
index 41afea84b7..0ee3cb46a5 100644
--- a/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c
+++ b/graalpython/com.oracle.graal.python.cext/src/unicodeobject.c
@@ -666,6 +666,10 @@ unicode_check_encoding_errors(const char *encoding, const char *errors)
int
_PyUnicode_CheckConsistency(PyObject *op, int check_content)
{
+ // GraalPy change: do not access managed unicode objects
+ if (points_to_py_handle_space(op))
+ return 1;
+
#define CHECK(expr) \
do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0)
diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java
index 523dd21cdb..7abb35da68 100644
--- a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java
+++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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
@@ -45,14 +45,18 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
@@ -64,15 +68,20 @@
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.AbstractAnnotationValueVisitor14;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import javax.tools.StandardLocation;
import com.oracle.graal.python.annotations.CApiConstants;
+import com.oracle.graal.python.annotations.CApiExternalFunctionSignatures;
import com.oracle.graal.python.annotations.CApiFields;
import com.oracle.graal.python.annotations.CApiStructs;
import com.sun.source.tree.VariableTree;
@@ -80,6 +89,12 @@
import com.sun.source.util.Trees;
public class CApiBuiltinsProcessor extends AbstractProcessor {
+ private static final String TRUFFLE_VIRTUAL_FRAME = "com.oracle.truffle.api.frame.VirtualFrame";
+ private static final String TRUFFLE_NODE = "com.oracle.truffle.api.nodes.Node";
+ private static final String NATIVE_FUNCTION_POINTER = "com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer";
+ private static final String PYTHON_CONTEXT = "com.oracle.graal.python.runtime.PythonContext";
+ private static final String TARGET_PACKAGE = "com.oracle.graal.python.builtins.modules.cext";
+
private static class ArgDescriptorsTreeScanner extends TreePathScanner
*/
- @CApiBuiltin(ret = Void, args = {Pointer, Pointer, Int}, call = Ignored)
- abstract static class GraalPyPrivate_Object_ReplicateNativeReferences extends CApiTernaryBuiltinNode {
- private static final Level LEVEL = Level.FINER;
-
- @Specialization(guards = "isNativeAccessAllowed()")
- static Object doGeneric(Object pointer, Object listHead, int n,
- @Bind Node inliningTarget,
- @Cached CStructAccess.ReadObjectNode readObjectNode,
- @Cached CStructAccess.ReadPointerNode readPointerNode,
- @Cached CoerceNativePointerToLongNode coerceNativePointerToLongNode,
- @Cached GcNativePtrToPythonNode gcNativePtrToPythonNode) {
- assert PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC);
-
- boolean loggable = GC_LOGGER.isLoggable(LEVEL);
- long lPointer = coerceNativePointerToLongNode.execute(inliningTarget, pointer);
- assert lPointer != 0;
- Object object = gcNativePtrToPythonNode.execute(inliningTarget, lPointer);
+ private static final Level REPLICATE_NATIVE_REFERENCES_LEVEL = Level.FINER;
- /*
- * If 'object' is null, there is no 'PythonAbstractNativeObject' wrapper for the native
- * object. This means that the native object is not referenced from managed code. So, we
- * don't need to replicate native references.
- */
+ @CApiBuiltin(ret = Void, args = {Pointer, Pointer, Int}, call = Ignored)
+ static void GraalPyPrivate_Object_ReplicateNativeReferences(long lPointer, long listHead, int n) {
+ assert PythonLanguage.get(null).getEngineOption(PythonOptions.PythonGC);
- Object repr = object;
- Object[] referents = null;
- if (object instanceof PythonAbstractNativeObject || object instanceof PythonModule || isTupleWithNativeStorage(object) || isListWithNativeStorage(object)) {
- /*
- * Note: it is important that we first collect the objects such that we have strong
- * Java references to them on the Java stack and then we overwrite the
- * 'replicatedNativeReferences' field. This is because the referents may already be
- * weakly referenced from the handle table and such referents may already be in the
- * previous array and then it could happen, that they die during list processing.
- */
- Object[] oldReferents;
- referents = new Object[n];
- if (object instanceof PythonAbstractNativeObject nativeObject) {
- if (loggable) {
- repr = nativeObject.toStringWithContext();
- }
- oldReferents = nativeObject.getReplicatedNativeReferences();
- nativeObject.setReplicatedNativeReferences(referents);
- } else if (object instanceof PythonModule module) {
- oldReferents = module.getReplicatedNativeReferences();
- module.setReplicatedNativeReferences(referents);
- } else {
- assert isTupleWithNativeStorage(object) || isListWithNativeStorage(object);
- NativeSequenceStorage nativeSequenceStorage = getNativeSequenceStorage(object);
- oldReferents = nativeSequenceStorage.getReplicatedNativeReferences();
- nativeSequenceStorage.setReplicatedNativeReferences(referents);
- }
- // Collect referents (traverse native list and resolve pointers)
- Object cur = listHead;
- for (int i = 0; i < n; i++) {
- referents[i] = readObjectNode.read(cur, GraalPyGC_CycleNode__item);
- cur = readPointerNode.read(cur, GraalPyGC_CycleNode__next);
- }
+ boolean loggable = GC_LOGGER.isLoggable(REPLICATE_NATIVE_REFERENCES_LEVEL);
+ assert lPointer != 0;
+ Object object = GcNativePtrToPythonNode.executeUncached(lPointer);
- /*
- * As described above: Ensure that the 'old' replicated references are strong until
- * this point. Otherwise, weakly referenced managed objects could die.
- */
- java.lang.ref.Reference.reachabilityFence(oldReferents);
+ /*
+ * If 'object' is null, there is no 'PythonAbstractNativeObject' wrapper for the native
+ * object. This means that the native object is not referenced from managed code. So, we
+ * don't need to replicate native references.
+ */
+ Object repr = object;
+ Object[] referents = null;
+ if (object instanceof PythonAbstractNativeObject || object instanceof PythonModule || isTupleWithNativeStorage(object) || isListWithNativeStorage(object)) {
+ /*
+ * Note: it is important that we first collect the objects such that we have strong Java
+ * references to them on the Java stack and then we overwrite the
+ * 'replicatedNativeReferences' field. This is because the referents may already be
+ * weakly referenced from the handle table and such referents may already be in the
+ * previous array and then it could happen, that they die during list processing.
+ */
+ Object[] oldReferents;
+ referents = new Object[n];
+ if (object instanceof PythonAbstractNativeObject nativeObject) {
if (loggable) {
- GC_LOGGER.log(LEVEL, PythonUtils.formatJString("Replicated native refs of %s to managed: %s", repr, arraysToString(referents)));
+ repr = nativeObject.toStringWithContext();
}
- } else if (object == null && loggable) {
- GC_LOGGER.log(LEVEL, PythonUtils.formatJString("Did not replicate native refs of %s: no wrapper", CApiContext.asHex(lPointer)));
+ oldReferents = nativeObject.getReplicatedNativeReferences();
+ nativeObject.setReplicatedNativeReferences(referents);
+ } else if (object instanceof PythonModule module) {
+ oldReferents = module.getReplicatedNativeReferences();
+ module.setReplicatedNativeReferences(referents);
+ } else {
+ assert isTupleWithNativeStorage(object) || isListWithNativeStorage(object);
+ NativeSequenceStorage nativeSequenceStorage = getNativeSequenceStorage(object);
+ oldReferents = nativeSequenceStorage.getReplicatedNativeReferences();
+ nativeSequenceStorage.setReplicatedNativeReferences(referents);
+ }
+ // Collect referents (traverse native list and resolve pointers)
+ long cur = listHead;
+ CStructAccess.ReadObjectNode readObjectNode = CStructAccess.ReadObjectNode.getUncached();
+ for (int i = 0; i < n; i++) {
+ referents[i] = readObjectNode.read(cur, GraalPyGC_CycleNode__item);
+ cur = readPtrField(cur, GraalPyGC_CycleNode__next);
}
- return PNone.NO_VALUE;
- }
- private static NativeSequenceStorage getNativeSequenceStorage(Object object) {
- NativeSequenceStorage nativeSequenceStorage;
- if (object instanceof PTuple tuple) {
- // cast is ensured by 'isTupleWithNativeStorage'
- nativeSequenceStorage = (NativeSequenceStorage) tuple.getSequenceStorage();
- } else {
- assert object instanceof PList;
- // casts are ensured by 'isListWithNativeStorage'
- nativeSequenceStorage = (NativeSequenceStorage) ((PList) object).getSequenceStorage();
+ /*
+ * As described above: Ensure that the 'old' replicated references are strong until this
+ * point. Otherwise, weakly referenced managed objects could die.
+ */
+ java.lang.ref.Reference.reachabilityFence(oldReferents);
+
+ if (loggable) {
+ GC_LOGGER.log(REPLICATE_NATIVE_REFERENCES_LEVEL, PythonUtils.formatJString("Replicated native refs of %s to managed: %s", repr, arraysToString(referents)));
}
- return nativeSequenceStorage;
+ } else if (object == null && loggable) {
+ GC_LOGGER.log(REPLICATE_NATIVE_REFERENCES_LEVEL, PythonUtils.formatJString("Did not replicate native refs of %s: no wrapper", CApiContext.asHex(lPointer)));
}
+ }
- @Specialization(guards = "!isNativeAccessAllowed()")
- @SuppressWarnings("unused")
- static Object doManaged(Object pointer, Object listHead, int n) {
- return PNone.NO_VALUE;
+ private static NativeSequenceStorage getNativeSequenceStorage(Object object) {
+ NativeSequenceStorage nativeSequenceStorage;
+ if (object instanceof PTuple tuple) {
+ // cast is ensured by 'isTupleWithNativeStorage'
+ nativeSequenceStorage = (NativeSequenceStorage) tuple.getSequenceStorage();
+ } else {
+ assert object instanceof PList;
+ // casts are ensured by 'isListWithNativeStorage'
+ nativeSequenceStorage = (NativeSequenceStorage) ((PList) object).getSequenceStorage();
}
+ return nativeSequenceStorage;
+ }
- @TruffleBoundary
- private static String arraysToString(Object[] arr) {
- return Arrays.toString(arr);
- }
+ @TruffleBoundary
+ private static String arraysToString(Object[] arr) {
+ return Arrays.toString(arr);
+ }
- private static boolean isTupleWithNativeStorage(Object object) {
- return object instanceof PTuple tuple && tuple.getSequenceStorage() instanceof NativeSequenceStorage;
- }
+ private static boolean isTupleWithNativeStorage(Object object) {
+ return object instanceof PTuple tuple && tuple.getSequenceStorage() instanceof NativeSequenceStorage;
+ }
- private static boolean isListWithNativeStorage(Object object) {
- return object instanceof PList list && list.getSequenceStorage() instanceof NativeSequenceStorage;
- }
+ private static boolean isListWithNativeStorage(Object object) {
+ return object instanceof PList list && list.getSequenceStorage() instanceof NativeSequenceStorage;
}
/**
@@ -1516,123 +1356,87 @@ private static boolean isListWithNativeStorage(Object object) {
* that involve managed objects.
*/
@CApiBuiltin(ret = Void, args = {Pointer}, call = Ignored)
- abstract static class GraalPyPrivate_Object_GC_EnsureWeak extends CApiUnaryBuiltinNode {
- @Specialization(guards = "isNativeAccessAllowed()")
- static Object doNative(Object weakCandidates,
- @Bind Node inliningTarget,
- @Cached CoerceNativePointerToLongNode coerceToLongNode,
- @Cached CStructAccess.ReadI64Node readI64Node,
- @Cached CStructAccess.WriteLongNode writeLongNode,
- @Cached NativePtrToPythonWrapperNode nativePtrToPythonWrapperNode,
- @Cached UpdateStrongRefNode updateRefNode) {
- // guaranteed by the guard
- assert PythonContext.get(inliningTarget).isNativeAccessAllowed();
- assert PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC);
+ static void GraalPyPrivate_Object_GC_EnsureWeak(long head) {
+ assert PythonContext.get(null).isNativeAccessAllowed();
+ assert PythonLanguage.get(null).getEngineOption(PythonOptions.PythonGC);
- /*
- * The list's head is a dummy node that can not be a tagged pointer because it is not an
- * object and always allocated in native.
- */
- long head = coerceToLongNode.execute(inliningTarget, weakCandidates);
- assert !HandlePointerConverter.pointsToPyHandleSpace(head);
+ HandleContext handleContext = PythonContext.get(null).handleContext;
- // PyGC_Head *gc = GC_NEXT(head)
- long gc = readI64Node.read(head, CFields.PyGC_Head___gc_next);
- /*
- * The list's head is not polluted with NEXT_MASK_UNREACHABLE. See 'move_weak_reachable'
- * at the end of the function.
- */
- assert (gc & NEXT_MASK_UNREACHABLE) == 0;
- while (gc != head) {
- assert (gc & NEXT_MASK_UNREACHABLE) == 0;
+ /*
+ * The list's head is a dummy node that can not be a tagged pointer because it is not an
+ * object and always allocated in native.
+ */
+ assert !HandlePointerConverter.pointsToPyHandleSpace(head);
- // PyObject *op = FROM_GC(gc)
- long op = gc + CStructs.PyGC_Head.size();
+ // PyGC_Head *gc = GC_NEXT(head)
+ long gc = readLongField(head, CFields.PyGC_Head___gc_next);
+ /*
+ * The list's head is not polluted with NEXT_MASK_UNREACHABLE. See 'move_weak_reachable' at
+ * the end of the function.
+ */
+ assert (gc & NEXT_MASK_UNREACHABLE) == 0;
+ while (gc != head) {
+ assert (gc & NEXT_MASK_UNREACHABLE) == 0;
- PythonNativeWrapper wrapper = nativePtrToPythonWrapperNode.execute(inliningTarget, op, true);
- if (wrapper instanceof PythonAbstractObjectNativeWrapper abstractObjectNativeWrapper) {
- if (GC_LOGGER.isLoggable(Level.FINE)) {
- GC_LOGGER.fine(PythonUtils.formatJString("Transitioning to weak reference to break a reference cycle for %s, refcount=%d",
- abstractObjectNativeWrapper.ref, abstractObjectNativeWrapper.getRefCount()));
- }
- updateRefNode.clearStrongRefButKeepInGCList(inliningTarget, abstractObjectNativeWrapper);
- }
+ // PyObject *op = FROM_GC(gc)
+ long op = gc + CStructs.PyGC_Head.size();
- // next = GC_NEXT(gc)
- long gcUntagged = HandlePointerConverter.pointerToStub(gc);
- long nextTaggedWithMask = readI64Node.read(gcUntagged, CFields.PyGC_Head___gc_next);
- // remove NEXT_MASK_UNREACHABLE flag
- long next = nextTaggedWithMask & ~NEXT_MASK_UNREACHABLE;
- /*
- * We expect to process 'weak_candidates' which all have NEXT_MASK_UNREACHABLE set
- * except of the list head (which is a dummy node)
- */
- assert next == head || (nextTaggedWithMask & NEXT_MASK_UNREACHABLE) != 0;
-
- /*
- * This is a "dirty" untrack since we just overwrite '_gc_prev' and '_gc_next' with
- * zero. Here it is fine because (a) managed objects will never have flags set in
- * '_gc_prev' that need to be preserved, and (b) because we untrack all objects in
- * this list anyway.
- */
- writeLongNode.write(gcUntagged, CFields.PyGC_Head___gc_next, 0);
- writeLongNode.write(gcUntagged, CFields.PyGC_Head___gc_prev, 0);
-
- gc = next;
+ if (HandlePointerConverter.pointsToPyHandleSpace(op)) {
+ int hti = readIntField(HandlePointerConverter.pointerToStub(op), CFields.GraalPyObject__handle_table_index);
+ UpdateHandleTableReferenceNode.clearStrongRefButKeepInGCListUncached(handleContext, op, hti);
+ } else {
+ // TODO(fa): investigate if that case is valid and necessary
+ throw CompilerDirectives.shouldNotReachHere();
}
- return PNone.NO_VALUE;
- }
- @Specialization(guards = "!isNativeAccessAllowed()")
- static Object doNative(@SuppressWarnings("unused") Object weakCandidates) {
- return PNone.NO_VALUE;
+ // next = GC_NEXT(gc)
+ long gcUntagged = HandlePointerConverter.pointerToStub(gc);
+ long nextTaggedWithMask = readLongField(gcUntagged, CFields.PyGC_Head___gc_next);
+ // remove NEXT_MASK_UNREACHABLE flag
+ long next = nextTaggedWithMask & ~NEXT_MASK_UNREACHABLE;
+ /*
+ * We expect to process 'weak_candidates' which all have NEXT_MASK_UNREACHABLE set
+ * except of the list head (which is a dummy node)
+ */
+ assert next == head || (nextTaggedWithMask & NEXT_MASK_UNREACHABLE) != 0;
+
+ /*
+ * This is a "dirty" untrack since we just overwrite '_gc_prev' and '_gc_next' with
+ * zero. Here it is fine because (a) managed objects will never have flags set in
+ * '_gc_prev' that need to be preserved, and (b) because we untrack all objects in this
+ * list anyway.
+ */
+ writeLongField(gcUntagged, CFields.PyGC_Head___gc_next, 0);
+ writeLongField(gcUntagged, CFields.PyGC_Head___gc_prev, 0);
+
+ gc = next;
}
}
@CApiBuiltin(ret = Int, args = {Pointer}, call = Ignored)
- abstract static class GraalPyPrivate_IsReferencedFromManaged extends CApiUnaryBuiltinNode {
- @Specialization(guards = "isNativeAccessAllowed()")
- static int doNative(Object pointer,
- @Bind Node inliningTarget,
- @Cached CoerceNativePointerToLongNode coerceToLongNode,
- @Cached GcNativePtrToPythonNode gcNativePtrToPythonNode) {
- // guaranteed by the guard
- assert PythonContext.get(inliningTarget).isNativeAccessAllowed();
- assert PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC);
-
- long lPointer = coerceToLongNode.execute(inliningTarget, pointer);
- // this upcall doesn't make sense for managed objects
- assert !HandlePointerConverter.pointsToPyHandleSpace(lPointer);
+ static int GraalPyPrivate_IsReferencedFromManaged(long lPointer) {
+ assert PythonContext.get(null).isNativeAccessAllowed();
+ assert PythonLanguage.get(null).getEngineOption(PythonOptions.PythonGC);
- Object object = gcNativePtrToPythonNode.execute(inliningTarget, lPointer);
- return PInt.intValue(object != null);
- }
+ // this upcall doesn't make sense for managed objects
+ assert !HandlePointerConverter.pointsToPyHandleSpace(lPointer);
- @Specialization(guards = "!isNativeAccessAllowed()")
- static Object doManaged(@SuppressWarnings("unused") Object pointer) {
- return PInt.intValue(false);
- }
+ Object object = GcNativePtrToPythonNode.executeUncached(lPointer);
+ return PInt.intValue(object != null);
}
@CApiBuiltin(ret = Void, call = Ignored)
- abstract static class GraalPyPrivate_EnableReferneceQueuePolling extends CApiNullaryBuiltinNode {
- @Specialization
- static Object doGeneric(@Bind Node inliningTarget) {
- assert PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC);
- HandleContext handleContext = PythonContext.get(inliningTarget).nativeContext;
- CApiTransitions.enableReferenceQueuePolling(handleContext);
- return PNone.NO_VALUE;
- }
+ static void GraalPyPrivate_EnableReferenceQueuePolling() {
+ assert PythonLanguage.get(null).getEngineOption(PythonOptions.PythonGC);
+ HandleContext handleContext = PythonContext.get(null).handleContext;
+ CApiTransitions.enableReferenceQueuePolling(handleContext);
}
@CApiBuiltin(ret = Int, call = Ignored)
- abstract static class GraalPyPrivate_DisableReferneceQueuePolling extends CApiNullaryBuiltinNode {
- @Specialization
- static int doGeneric(@Bind Node inliningTarget) {
- assert PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC);
- HandleContext handleContext = PythonContext.get(inliningTarget).nativeContext;
- return PInt.intValue(CApiTransitions.disableReferenceQueuePolling(handleContext));
- }
+ static int GraalPyPrivate_DisableReferenceQueuePolling() {
+ assert PythonLanguage.get(null).getEngineOption(PythonOptions.PythonGC);
+ HandleContext handleContext = PythonContext.get(null).handleContext;
+ return PInt.intValue(CApiTransitions.disableReferenceQueuePolling(handleContext));
}
private static final int TRACE_MEM = 0x1;
@@ -1653,136 +1457,117 @@ static int doGeneric(@Bind Node inliningTarget) {
* with @EngineOption so they are sure to be the same, or that options differing is benign.
*/
@CApiBuiltin(ret = Int, call = Ignored)
- abstract static class GraalPyPrivate_Native_Options extends CApiNullaryBuiltinNode {
-
- @Specialization
- @TruffleBoundary
- int getNativeOptions() {
- int options = 0;
- PythonLanguage language = PythonLanguage.get(null);
- if (language.getEngineOption(PythonOptions.TraceNativeMemory)) {
- options |= TRACE_MEM;
- }
- if (LOGGER.isLoggable(Level.INFO)) {
- options |= LOG_INFO;
- }
- if (LOGGER.isLoggable(Level.CONFIG)) {
- options |= LOG_CONFIG;
- }
- if (LOGGER.isLoggable(Level.FINE)) {
- options |= LOG_FINE;
- }
- if (LOGGER.isLoggable(Level.FINER)) {
- options |= LOG_FINER;
- }
- if (LOGGER.isLoggable(Level.FINEST)) {
- options |= LOG_FINEST;
- }
- if (PythonContext.DEBUG_CAPI) {
- options |= DEBUG_CAPI;
- }
- if (language.getEngineOption(PythonOptions.PythonGC)) {
- options |= PYTHON_GC;
- }
- if (language.getEngineOption(PythonOptions.PoisonNativeMemoryOnFree)) {
- options |= POISON_NATIVE_MEMORY_ON_FREE;
- }
- if (language.getEngineOption(PythonOptions.SampleNativeMemoryAllocSites)) {
- options |= SAMPLE_NATIVE_MEMORY_ALLOC_SITES;
- }
- return options;
+ @TruffleBoundary
+ static int GraalPyPrivate_Native_Options() {
+ int options = 0;
+ PythonLanguage language = PythonLanguage.get(null);
+ if (language.getEngineOption(PythonOptions.TraceNativeMemory)) {
+ options |= TRACE_MEM;
+ }
+ if (LOGGER.isLoggable(Level.INFO)) {
+ options |= LOG_INFO;
+ }
+ if (LOGGER.isLoggable(Level.CONFIG)) {
+ options |= LOG_CONFIG;
+ }
+ if (LOGGER.isLoggable(Level.FINE)) {
+ options |= LOG_FINE;
}
+ if (LOGGER.isLoggable(Level.FINER)) {
+ options |= LOG_FINER;
+ }
+ if (LOGGER.isLoggable(Level.FINEST)) {
+ options |= LOG_FINEST;
+ }
+ if (PythonContext.DEBUG_CAPI) {
+ options |= DEBUG_CAPI;
+ }
+ if (language.getEngineOption(PythonOptions.PythonGC)) {
+ options |= PYTHON_GC;
+ }
+ if (language.getEngineOption(PythonOptions.PoisonNativeMemoryOnFree)) {
+ options |= POISON_NATIVE_MEMORY_ON_FREE;
+ }
+ if (language.getEngineOption(PythonOptions.SampleNativeMemoryAllocSites)) {
+ options |= SAMPLE_NATIVE_MEMORY_ALLOC_SITES;
+ }
+ return options;
}
- @CApiBuiltin(ret = Void, args = {Int, ConstCharPtrAsTruffleString}, call = Ignored)
- abstract static class GraalPyPrivate_LogString extends CApiBinaryBuiltinNode {
-
- @Specialization
- @TruffleBoundary
- static Object log(int level, TruffleString message) {
- String msg = message.toJavaStringUncached();
- switch (level) {
- case LOG_INFO:
- LOGGER.info(msg);
- break;
- case LOG_CONFIG:
- LOGGER.config(msg);
- break;
- case LOG_FINE:
- LOGGER.fine(msg);
- break;
- case LOG_FINER:
- LOGGER.finer(msg);
- break;
- case LOG_FINEST:
- LOGGER.finest(msg);
- break;
- default:
- throw CompilerDirectives.shouldNotReachHere("unknown log level: " + level);
- }
- return PNone.NO_VALUE;
+ @CApiBuiltin(ret = Void, args = {Int, ConstCharPtr}, call = Ignored)
+ @TruffleBoundary
+ static void GraalPyPrivate_LogString(int level, long messagePtr) {
+ TruffleString message = (TruffleString) CharPtrToPythonNode.getUncached().execute(messagePtr);
+ String msg = message.toJavaStringUncached();
+ switch (level) {
+ case LOG_INFO:
+ LOGGER.info(msg);
+ break;
+ case LOG_CONFIG:
+ LOGGER.config(msg);
+ break;
+ case LOG_FINE:
+ LOGGER.fine(msg);
+ break;
+ case LOG_FINER:
+ LOGGER.finer(msg);
+ break;
+ case LOG_FINEST:
+ LOGGER.finest(msg);
+ break;
+ default:
+ throw CompilerDirectives.shouldNotReachHere("unknown log level: " + level);
}
}
@CApiBuiltin(ret = Void, args = {}, call = Direct)
- abstract static class GraalPyPrivate_DebugTrace extends CApiNullaryBuiltinNode {
-
- @Specialization
- @TruffleBoundary
- Object trace() {
- PrintStream out = new PrintStream(getContext().getEnv().out());
- if (getContext().getOption(PythonOptions.EnableDebuggingBuiltins)) {
- out.println("\n\nJava Stacktrace:");
- new RuntimeException().printStackTrace(out);
- out.println("\n\nTruffle Stacktrace:");
- printStack();
- out.println("\n\nFrames:");
- Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() {
-
- public Void visitFrame(FrameInstance frame) {
- out.println(" ===========================");
- out.println(" call: " + frame.getCallNode());
- out.println(" target: " + frame.getCallTarget());
- Frame f = frame.getFrame(FrameInstance.FrameAccess.READ_ONLY);
- out.println(" args: " + Arrays.asList(f.getArguments()));
- return null;
- }
- });
- } else {
- out.println("\n\nDEBUG TRACE (enable details via --python.EnableDebuggingBuiltins)");
- }
- return PNone.NO_VALUE;
+ @TruffleBoundary
+ static void GraalPyPrivate_DebugTrace() {
+ PythonContext context = PythonContext.get(null);
+ PrintStream out = new PrintStream(context.getEnv().out());
+ if (context.getOption(PythonOptions.EnableDebuggingBuiltins)) {
+ out.println("\n\nJava Stacktrace:");
+ new RuntimeException().printStackTrace(out);
+ out.println("\n\nTruffle Stacktrace:");
+ PNodeWithContext.printStack();
+ out.println("\n\nFrames:");
+ Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() {
+
+ public Void visitFrame(FrameInstance frame) {
+ out.println(" ===========================");
+ out.println(" call: " + frame.getCallNode());
+ out.println(" target: " + frame.getCallTarget());
+ Frame f = frame.getFrame(FrameInstance.FrameAccess.READ_ONLY);
+ out.println(" args: " + Arrays.asList(f.getArguments()));
+ return null;
+ }
+ });
+ } else {
+ out.println("\n\nDEBUG TRACE (enable details via --python.EnableDebuggingBuiltins)");
}
}
@CApiBuiltin(ret = Int, args = {Pointer}, call = Direct)
- abstract static class GraalPyPrivate_Debug extends CApiUnaryBuiltinNode {
- @Specialization
- @TruffleBoundary
- static Object doIt(Object arg,
- @Cached DebugNode debugNode) {
- debugNode.execute(new Object[]{arg});
- return 0;
- }
+ @TruffleBoundary
+ static int GraalPyPrivate_Debug(long arg) {
+ DebugNode.tdebug(PythonContext.get(null), new Object[]{arg});
+ return 0;
}
@CApiBuiltin(ret = Int, args = {Pointer}, call = Direct)
- abstract static class GraalPyPrivate_ToNative extends CApiUnaryBuiltinNode {
- @Specialization
- @TruffleBoundary
- int doIt(Object object) {
- if (!PythonOptions.EnableDebuggingBuiltins.getValue(getContext().getEnv().getOptions())) {
- String message = "GraalPyPrivate_ToNative is not enabled - enable with --python.EnableDebuggingBuiltins\n";
- try {
- getContext().getEnv().out().write(message.getBytes());
- } catch (IOException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- return 1;
+ @TruffleBoundary
+ static int GraalPyPrivate_ToNative(@SuppressWarnings("unused") long object) {
+ PythonContext context = PythonContext.get(null);
+ if (!PythonOptions.EnableDebuggingBuiltins.getValue(context.getEnv().getOptions())) {
+ String message = "GraalPyPrivate_ToNative is not enabled - enable with --python.EnableDebuggingBuiltins\n";
+ try {
+ context.getEnv().out().write(message.getBytes());
+ } catch (IOException e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
}
- InteropLibrary.getUncached().toNative(object);
- return 0;
+ return 1;
}
+ return 0;
}
/**
@@ -1822,121 +1607,103 @@ int doIt(Object object) {
*
*/
@CApiBuiltin(ret = Void, args = {Pointer}, call = Ignored)
- abstract static class GraalPyPrivate_InitBuiltinTypesAndStructs extends CApiUnaryBuiltinNode {
-
- @TruffleBoundary
- @Specialization
- Object doGeneric(Object builtinTypesArrayPointer) {
- List> builtinTypes = new LinkedList<>();
- PythonContext context = getContext();
- CStructAccess.ReadPointerNode readPointerNode = CStructAccess.ReadPointerNode.getUncached();
- try {
- // first phase: lookup built-in type by name, create wrappers and set native pointer
- InteropLibrary lib = null;
- for (int i = 0;; i += 2) {
- Object typeStructPtr = readPointerNode.readArrayElement(builtinTypesArrayPointer, i);
- /*
- * Most pointer types will be the same. So, we store the last looked up library
- * in a local variable. However, It may happen that there are different types of
- * pointer objects involved, so we need to update the library if the current one
- * does not accept the object.
- */
- if (lib == null || !lib.accepts(typeStructPtr)) {
- lib = InteropLibrary.getUncached(typeStructPtr);
- }
- // if we reach the sentinel, stop the loop
- if (lib.isNull(typeStructPtr)) {
- break;
- }
- Object namePtr = readPointerNode.readArrayElement(builtinTypesArrayPointer, i + 1);
- TruffleString name = FromCharPointerNodeGen.getUncached().execute(namePtr, false);
-
- // lookup the built-in type by name
- PythonManagedClass clazz = lookupBuiltinTypeWithName(context, name);
+ @TruffleBoundary
+ static void GraalPyPrivate_InitBuiltinTypesAndStructs(long builtinTypesArrayPointer) {
+ List builtinTypes = new LinkedList<>();
+ PythonContext context = PythonContext.get(null);
+ try {
+ // first phase: lookup built-in type by name, create wrappers and set native pointer
+ for (int i = 0;; i += 2) {
+ long typeStructPtr = readPtrArrayElement(builtinTypesArrayPointer, i);
+ // if we reach the sentinel, stop the loop
+ if (typeStructPtr == 0L) {
+ break;
+ }
+ long namePtr = readPtrArrayElement(builtinTypesArrayPointer, i + 1);
+ TruffleString name = FromCharPointerNode.executeUncached(namePtr, false);
- // create the wrapper and register the pointer
- LOGGER.fine(() -> "setting type store for built-in class " + name + " to " + PythonUtils.formatPointer(typeStructPtr));
- PythonClassNativeWrapper.wrapStaticTypeStructForManagedClass(clazz, TypeNodes.GetNameNode.executeUncached(clazz), typeStructPtr);
+ // lookup the built-in type by name
+ PythonManagedClass clazz = lookupBuiltinTypeWithName(context, name);
- builtinTypes.add(Pair.create(clazz, typeStructPtr));
- }
+ // create the lookup table entry and sync (selected) native type fields to the
+ // managed type
+ LOGGER.fine(() -> "setting type store for built-in class " + name + " to " + PythonUtils.formatPointer(typeStructPtr));
+ int typeLookupTableIdx = ToNativeTypeNode.wrapStaticTypeStructForManagedClass(clazz, typeStructPtr);
- // second phase: initialize the native type store
- for (Pair pair : builtinTypes) {
- LOGGER.fine(() -> "initializing built-in class " + TypeNodes.GetNameNode.executeUncached(pair.getLeft()));
- PythonClassNativeWrapper.initNative(pair.getLeft(), pair.getRight());
- }
+ builtinTypes.add(new ClassPtrPair(clazz, typeStructPtr, typeLookupTableIdx));
+ }
- return PNone.NO_VALUE;
- } catch (PException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
+ // second phase: initialize the native type store
+ for (ClassPtrPair pair : builtinTypes) {
+ LOGGER.fine(() -> "initializing built-in class " + TypeNodes.GetNameNode.executeUncached(pair.clazz()));
+ ToNativeTypeNode.initNative(pair.clazz, pair.ptr, pair.typeLookupTableIdx);
}
+ } catch (PException e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
}
+ }
- /**
- * Looks up a built-in type by name. This method may throw a Python exception (i.e.
- * {@code PException}) because if the type belongs to a built-in module, it needs to read an
- * attribute from the module.
- */
- private static PythonManagedClass lookupBuiltinTypeWithName(PythonContext context, TruffleString tsName) {
- Python3Core core = context.getCore();
- PythonManagedClass clazz = null;
- String name = tsName.toJavaStringUncached();
- // see if we're dealing with a type from a specific module
- int index = name.indexOf('.');
- if (index == -1) {
- for (PythonBuiltinClassType type : PythonBuiltinClassType.VALUES) {
- if (type.getName().equalsUncached(tsName, TS_ENCODING)) {
- clazz = core.lookupType(type);
- break;
- }
+ private record ClassPtrPair(PythonManagedClass clazz, long ptr, int typeLookupTableIdx) {
+ }
+
+ /**
+ * Looks up a built-in type by name. This method may throw a Python exception (i.e.
+ * {@code PException}) because if the type belongs to a built-in module, it needs to read an
+ * attribute from the module.
+ */
+ private static PythonManagedClass lookupBuiltinTypeWithName(PythonContext context, TruffleString tsName) {
+ Python3Core core = context.getCore();
+ PythonManagedClass clazz = null;
+ String name = tsName.toJavaStringUncached();
+ // see if we're dealing with a type from a specific module
+ int index = name.indexOf('.');
+ if (index == -1) {
+ for (PythonBuiltinClassType type : PythonBuiltinClassType.VALUES) {
+ if (type.getName().equalsUncached(tsName, TS_ENCODING)) {
+ clazz = core.lookupType(type);
+ break;
}
- } else {
- String module = name.substring(0, index);
- name = name.substring(index + 1);
- TruffleString tsModule = toTruffleStringUncached(module);
- Object moduleObject = core.lookupBuiltinModule(tsModule);
+ }
+ } else {
+ String module = name.substring(0, index);
+ name = name.substring(index + 1);
+ TruffleString tsModule = toTruffleStringUncached(module);
+ Object moduleObject = core.lookupBuiltinModule(tsModule);
+ if (moduleObject == null) {
+ moduleObject = AbstractImportNode.lookupImportedModule(context, tsModule);
if (moduleObject == null) {
- moduleObject = AbstractImportNode.lookupImportedModule(context, tsModule);
- if (moduleObject == null) {
- throw CompilerDirectives.shouldNotReachHere(String.format(
- "Module '%s' is needed during C API initialization, but was not imported prior to the initialization in ensureCapiWasLoaded. This is an internal error in GraalPy.",
- module));
- }
- }
- // Assumption: builtin modules' tp_getattro is well-behaved and just reads the
- // attribute, there is no locking or blocking inside
- Object attribute = PyObjectGetAttr.getUncached().execute(null, moduleObject, toTruffleStringUncached(name));
- if (attribute != PNone.NO_VALUE) {
- if (attribute instanceof PythonBuiltinClassType builtinType) {
- clazz = core.lookupType(builtinType);
- } else {
- clazz = (PythonManagedClass) attribute;
- }
+ throw CompilerDirectives.shouldNotReachHere(String.format(
+ "Module '%s' is needed during C API initialization, but was not imported prior to the initialization in ensureCapiWasLoaded. This is an internal error in GraalPy.",
+ module));
}
-
}
- if (clazz == null) {
- throw CompilerDirectives.shouldNotReachHere("cannot find class " + name);
+ // Assumption: builtin modules' tp_getattro is well-behaved and just reads the
+ // attribute, there is no locking or blocking inside
+ Object attribute = PyObjectGetAttr.getUncached().execute(null, moduleObject, toTruffleStringUncached(name));
+ if (attribute != PNone.NO_VALUE) {
+ if (attribute instanceof PythonBuiltinClassType builtinType) {
+ clazz = core.lookupType(builtinType);
+ } else {
+ clazz = (PythonManagedClass) attribute;
+ }
}
- return clazz;
}
- }
+ if (clazz == null) {
+ throw CompilerDirectives.shouldNotReachHere("cannot find class " + name);
+ }
- @CApiBuiltin(ret = CHAR_PTR, args = {PyObject}, call = Ignored)
- abstract static class GraalPyPrivate_GetMMapData extends CApiUnaryBuiltinNode {
+ return clazz;
+ }
- @Specialization
- Object get(PMMap object,
- @Bind Node inliningTarget,
- @CachedLibrary("getPosixSupport()") PosixSupportLibrary posixLib,
- @Cached PConstructAndRaiseNode.Lazy raiseNode) {
- try {
- return posixLib.mmapGetPointer(getPosixSupport(), object.getPosixSupportHandle());
- } catch (PosixSupportLibrary.UnsupportedPosixFeatureException e) {
- throw raiseNode.get(inliningTarget).raiseOSErrorUnsupported(null, e);
- }
+ @CApiBuiltin(ret = CHAR_PTR, args = {PyObjectRawPointer}, call = Ignored)
+ static long GraalPyPrivate_GetMMapData(long objectPtr) {
+ PMMap object = (PMMap) NativeToPythonNode.executeRawUncached(objectPtr);
+ PythonContext context = PythonContext.get(null);
+ try {
+ return PosixSupportLibrary.getUncached().mmapGetPointer(context.getPosixSupport(), object.getPosixSupportHandle());
+ } catch (PosixSupportLibrary.UnsupportedPosixFeatureException e) {
+ throw PConstructAndRaiseNode.getUncached().raiseOSErrorUnsupported(null, e);
}
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextByteArrayBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextByteArrayBuiltins.java
index 5d3ab2445f..f87d137d5d 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextByteArrayBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextByteArrayBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -42,53 +42,36 @@
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.CHAR_PTR;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectRawPointer;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.getFieldPtr;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
-import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode;
import com.oracle.graal.python.builtins.objects.bytes.PByteArray;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
import com.oracle.graal.python.builtins.objects.cext.capi.PySequenceArrayWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
-import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode;
+import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.nodes.Node;
public final class PythonCextByteArrayBuiltins {
- @CApiBuiltin(ret = CHAR_PTR, args = {PyObject}, call = Direct)
- abstract static class PyByteArray_AsString extends CApiUnaryBuiltinNode {
- @Specialization
- static Object doByteArray(PByteArray bytes) {
+ @CApiBuiltin(ret = CHAR_PTR, args = {PyObjectRawPointer}, call = Direct)
+ static long PyByteArray_AsString(long bytesPtr) {
+ Object obj = NativeToPythonNode.executeRawUncached(bytesPtr);
+ if (obj instanceof PByteArray bytes) {
return PySequenceArrayWrapper.ensureNativeSequence(bytes);
}
-
- @Specialization
- static Object doNative(PythonAbstractNativeObject obj,
- @Bind Node inliningTarget,
- @Cached GetPythonObjectClassNode getClassNode,
- @Cached IsSubtypeNode isSubtypeNode,
- @Cached CStructAccess.GetElementPtrNode getArray,
- @Cached PRaiseNode raiseNode) {
- if (isSubtypeNode.execute(getClassNode.execute(inliningTarget, obj), PythonBuiltinClassType.PByteArray)) {
- return getArray.getElementPtr(obj.getPtr(), CFields.PyByteArrayObject__ob_start);
+ if (obj instanceof PythonAbstractNativeObject nativeObj) {
+ Object type = GetClassNode.executeUncached(nativeObj);
+ if (IsSubtypeNode.getUncached().execute(type, PythonBuiltinClassType.PByteArray)) {
+ return getFieldPtr(nativeObj.getPtr(), CFields.PyByteArrayObject__ob_start);
}
- return doError(obj, raiseNode);
- }
-
- @Fallback
- static Object doError(Object obj,
- @Bind Node inliningTarget) {
- throw PRaiseNode.raiseStatic(inliningTarget, PythonErrorType.TypeError, ErrorMessages.EXPECTED_S_P_FOUND, "bytearray", obj);
}
+ throw PRaiseNode.raiseStatic(null, PythonErrorType.TypeError, ErrorMessages.EXPECTED_S_P_FOUND, "bytearray", obj);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBytesBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBytesBuiltins.java
index d0ad4f3905..1aeb3fce1d 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBytesBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBytesBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -50,11 +50,13 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtrAsTruffleString;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectRawPointer;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
+import static com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.getByteArray;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyVarObject__ob_size;
-
-import java.util.Arrays;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.getFieldPtr;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
@@ -66,19 +68,14 @@
import com.oracle.graal.python.builtins.objects.bytes.BytesCommonBuiltins;
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes.GetBytesStorage;
-import com.oracle.graal.python.builtins.objects.bytes.PByteArray;
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
import com.oracle.graal.python.builtins.objects.bytes.PBytesLike;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CApiGuards;
import com.oracle.graal.python.builtins.objects.cext.capi.PySequenceArrayWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.GetByteArrayNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetItemScalarNode;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.builtins.objects.str.StringBuiltins.EncodeNode;
@@ -89,7 +86,7 @@
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
-import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode;
+import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.nodes.util.CastToByteNode;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
import com.oracle.graal.python.runtime.object.PFactory;
@@ -97,58 +94,37 @@
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
import com.oracle.graal.python.util.OverflowException;
import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
-import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.strings.TruffleString;
public final class PythonCextBytesBuiltins {
- @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Direct)
- abstract static class PyBytes_Size extends CApiUnaryBuiltinNode {
- @Specialization
- static long doPBytes(PBytes obj,
- @Bind Node inliningTarget,
- @Cached PyObjectSizeNode sizeNode) {
- return sizeNode.execute(null, inliningTarget, obj);
- }
-
- @Specialization
- static long doOther(PythonAbstractNativeObject obj,
- @Bind Node inliningTarget,
- @Cached PyBytesCheckNode check,
- @Cached CStructAccess.ReadI64Node readI64Node) {
- if (check.execute(inliningTarget, obj)) {
- return readI64Node.readFromObj(obj, PyVarObject__ob_size);
- }
- return fallback(obj, inliningTarget);
+ @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer}, call = Direct)
+ static long PyBytes_Size(long objPtr) {
+ Object obj = NativeToPythonNode.executeRawUncached(objPtr);
+ if (obj instanceof PBytes bytes) {
+ return PyObjectSizeNode.executeUncached(bytes);
}
-
- @Fallback
- @TruffleBoundary
- static long fallback(Object obj,
- @Bind Node inliningTarget) {
- throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.EXPECTED_BYTES_P_FOUND, obj);
+ if (obj instanceof PythonAbstractNativeObject nativeObj && PyBytesCheckNode.executeUncached(nativeObj)) {
+ return readLongField(nativeObj.getPtr(), PyVarObject__ob_size);
}
+ throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.EXPECTED_BYTES_P_FOUND, obj);
}
- @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored)
- abstract static class GraalPyPrivate_Bytes_Concat extends CApiBinaryBuiltinNode {
- @Specialization
- static Object concat(Object original, Object newPart,
- @Cached BytesCommonBuiltins.ConcatNode addNode) {
- return addNode.execute(null, original, newPart);
- }
+ @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored)
+ static long GraalPyPrivate_Bytes_Concat(long originalPtr, long newPartPtr) {
+ Object original = NativeToPythonNode.executeRawUncached(originalPtr);
+ Object newPart = NativeToPythonNode.executeRawUncached(newPartPtr);
+ Object result = BytesCommonBuiltins.ConcatNode.executeUncached(original, newPart);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
}
+ // TODO(CAPI STATIC): uses nodes without @GenerateUncached
@CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct)
abstract static class _PyBytes_Join extends CApiBinaryBuiltinNode {
@Specialization
@@ -158,6 +134,7 @@ static Object join(Object original, Object newPart,
}
}
+ // TODO(CAPI STATIC): uses nodes without @GenerateUncached
@CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtrAsTruffleString, PyObject}, call = Ignored)
abstract static class GraalPyPrivate_Bytes_FromFormat extends CApiBinaryBuiltinNode {
@Specialization
@@ -169,6 +146,7 @@ static Object fromFormat(TruffleString fmt, Object args,
}
}
+ // TODO(CAPI STATIC): uses nodes without @GenerateUncached
@CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct)
abstract static class PyBytes_FromObject extends CApiUnaryBuiltinNode {
@Specialization(guards = "isBuiltinBytes(bytes)")
@@ -185,246 +163,97 @@ static Object fromObject(Object obj,
}
}
- @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtr, Py_ssize_t}, call = Ignored)
- @ImportStatic(CApiGuards.class)
- abstract static class GraalPyPrivate_Bytes_FromStringAndSize extends CApiBinaryBuiltinNode {
- // n.b.: the specializations for PIBytesLike are quite common on
- // managed, when the PySequenceArrayWrapper that we used never went
- // native, and during the upcall to here it was simply unwrapped again
- // with the ToJava (rather than mapped from a native pointer back into a
- // PythonNativeObject)
-
- @Specialization
- static Object doGeneric(PythonNativeWrapper object, long size,
- @Bind PythonLanguage language,
- @Cached NativeToPythonNode asPythonObjectNode,
- @Exclusive @Cached BytesNodes.ToBytesNode getByteArrayNode) {
- byte[] ary = getByteArrayNode.execute(null, asPythonObjectNode.execute(object));
- if (size >= 0 && size < ary.length) {
- // cast to int is guaranteed because of 'size < ary.length'
- return PFactory.createBytes(language, Arrays.copyOf(ary, (int) size));
- } else {
- return PFactory.createBytes(language, ary);
- }
- }
-
- @Specialization(guards = "!isNativeWrapper(nativePointer)")
- static Object doNativePointer(Object nativePointer, long size,
- @Bind Node inliningTarget,
- @Bind PythonLanguage language,
- @Exclusive @Cached GetByteArrayNode getByteArrayNode,
- @Cached PRaiseNode raiseNode) {
- try {
- return PFactory.createBytes(language, getByteArrayNode.execute(inliningTarget, nativePointer, size));
- } catch (InteropException e) {
- throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.M, e);
- } catch (OverflowException e) {
- throw raiseNode.raise(inliningTarget, PythonErrorType.SystemError, ErrorMessages.NEGATIVE_SIZE_PASSED);
- }
+ @CApiBuiltin(ret = PyObjectRawPointer, args = {ConstCharPtr, Py_ssize_t}, call = Ignored)
+ static long GraalPyPrivate_Bytes_FromStringAndSize(long nativePointer, long size) {
+ try {
+ byte[] bytes = getByteArray(nativePointer, size);
+ Object result = PFactory.createBytes(PythonLanguage.get(null), bytes);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (OverflowException e) {
+ throw PRaiseNode.raiseStatic(null, PythonErrorType.SystemError, ErrorMessages.NEGATIVE_SIZE_PASSED);
}
}
- @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtr, Py_ssize_t}, call = Ignored)
- @ImportStatic(CApiGuards.class)
- abstract static class GraalPyPrivate_ByteArray_FromStringAndSize extends CApiBinaryBuiltinNode {
- @Specialization
- static Object doGeneric(PythonNativeWrapper object, long size,
- @Bind PythonLanguage language,
- @Cached NativeToPythonNode asPythonObjectNode,
- @Exclusive @Cached BytesNodes.ToBytesNode getByteArrayNode) {
- byte[] ary = getByteArrayNode.execute(null, asPythonObjectNode.execute(object));
- if (size >= 0 && size < ary.length) {
- // cast to int is guaranteed because of 'size < ary.length'
- return PFactory.createByteArray(language, Arrays.copyOf(ary, (int) size));
- } else {
- return PFactory.createByteArray(language, ary);
- }
- }
-
- @Specialization(guards = "!isNativeWrapper(nativePointer)")
- static Object doNativePointer(Object nativePointer, long size,
- @Bind Node inliningTarget,
- @Bind PythonLanguage language,
- @Exclusive @Cached GetByteArrayNode getByteArrayNode,
- @Cached PRaiseNode raiseNode) {
- try {
- return PFactory.createByteArray(language, getByteArrayNode.execute(inliningTarget, nativePointer, size));
- } catch (InteropException e) {
- return raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.M, e);
- } catch (OverflowException e) {
- return raiseNode.raise(inliningTarget, PythonErrorType.SystemError, ErrorMessages.NEGATIVE_SIZE_PASSED);
- }
+ @CApiBuiltin(ret = PyObjectRawPointer, args = {ConstCharPtr, Py_ssize_t}, call = Ignored)
+ static long GraalPyPrivate_ByteArray_FromStringAndSize(long nativePointer, long size) {
+ try {
+ byte[] bytes = getByteArray(nativePointer, size);
+ Object result = PFactory.createByteArray(PythonLanguage.get(null), bytes);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (OverflowException e) {
+ throw PRaiseNode.raiseStatic(null, PythonErrorType.SystemError, ErrorMessages.NEGATIVE_SIZE_PASSED);
}
}
- @CApiBuiltin(name = "PyByteArray_Resize", ret = Int, args = {PyObject, Py_ssize_t}, call = Direct)
- @CApiBuiltin(ret = Int, args = {PyObject, Py_ssize_t}, call = Ignored)
- abstract static class GraalPyPrivate_Bytes_Resize extends CApiBinaryBuiltinNode {
-
- @Specialization
- static int resize(PBytesLike self, long newSizeL,
- @Bind Node inliningTarget,
- @Cached SequenceStorageNodes.GetItemNode getItemNode,
- @Cached PyNumberAsSizeNode asSizeNode,
- @Cached CastToByteNode castToByteNode) {
-
- SequenceStorage storage = self.getSequenceStorage();
- int newSize = asSizeNode.executeExact(null, inliningTarget, newSizeL);
- int len = storage.length();
- byte[] smaller = new byte[newSize];
- for (int i = 0; i < newSize && i < len; i++) {
- smaller[i] = castToByteNode.execute(null, getItemNode.execute(storage, i));
- }
- self.setSequenceStorage(new ByteSequenceStorage(smaller));
- return 0;
+ @CApiBuiltin(name = "PyByteArray_Resize", ret = Int, args = {PyObjectRawPointer, Py_ssize_t}, call = Direct)
+ @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, Py_ssize_t}, call = Ignored)
+ static int GraalPyPrivate_Bytes_Resize(long selfPtr, long newSizeL) {
+ Object self = NativeToPythonNode.executeRawUncached(selfPtr);
+ if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, !(self instanceof PBytesLike))) {
+ throw PRaiseNode.raiseStatic(null, SystemError, ErrorMessages.EXPECTED_S_NOT_P, "a bytes object", self);
}
-
- @Fallback
- static int fallback(Object self, @SuppressWarnings("unused") Object o,
- @Bind Node inliningTarget) {
- throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.EXPECTED_S_NOT_P, "a bytes object", self);
+ PBytesLike bytesLike = (PBytesLike) self;
+ SequenceStorage storage = bytesLike.getSequenceStorage();
+ int newSize = PyNumberAsSizeNode.executeExactUncached(newSizeL);
+ int len = storage.length();
+ byte[] resized = new byte[newSize];
+ CastToByteNode castToByteNode = CastToByteNode.getUncached();
+ for (int i = 0; i < newSize && i < len; i++) {
+ resized[i] = castToByteNode.execute(null, GetItemScalarNode.executeUncached(storage, i));
}
+ bytesLike.setSequenceStorage(new ByteSequenceStorage(resized));
+ return 0;
}
- @CApiBuiltin(ret = PyObjectTransfer, args = {ArgDescriptor.Long}, call = Ignored)
- abstract static class GraalPyPrivate_Bytes_EmptyWithCapacity extends CApiUnaryBuiltinNode {
-
- @Specialization
- static PBytes doInt(int size,
- @Bind PythonLanguage language) {
- return PFactory.createBytes(language, new byte[size]);
- }
-
- @Specialization(rewriteOn = OverflowException.class)
- static PBytes doLong(long size,
- @Bind PythonLanguage language) throws OverflowException {
- return doInt(PInt.intValueExact(size), language);
- }
-
- @Specialization(replaces = "doLong")
- static PBytes doLongOvf(long size,
- @Bind Node inliningTarget,
- @Bind PythonLanguage language,
- @Shared("raiseNode") @Cached PRaiseNode raiseNode) {
- try {
- return doInt(PInt.intValueExact(size), language);
- } catch (OverflowException e) {
- throw raiseNode.raise(inliningTarget, IndexError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, size);
- }
- }
-
- @Specialization(rewriteOn = OverflowException.class)
- static PBytes doPInt(PInt size,
- @Bind PythonLanguage language) throws OverflowException {
- return doInt(size.intValueExact(), language);
- }
-
- @Specialization(replaces = "doPInt")
- static PBytes doPIntOvf(PInt size,
- @Bind Node inliningTarget,
- @Bind PythonLanguage language,
- @Shared("raiseNode") @Cached PRaiseNode raiseNode) {
- try {
- return doInt(size.intValueExact(), language);
- } catch (OverflowException e) {
- throw raiseNode.raise(inliningTarget, IndexError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, size);
- }
+ @CApiBuiltin(ret = PyObjectRawPointer, args = {ArgDescriptor.Long}, call = Ignored)
+ static long GraalPyPrivate_Bytes_EmptyWithCapacity(long size) {
+ try {
+ Object result = PFactory.createBytes(PythonLanguage.get(null), new byte[PInt.intValueExact(size)]);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (OverflowException e) {
+ throw PRaiseNode.raiseStatic(null, IndexError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, size);
}
}
- @CApiBuiltin(ret = PyObjectTransfer, args = {Py_ssize_t}, call = Ignored)
- abstract static class GraalPyPrivate_ByteArray_EmptyWithCapacity extends CApiUnaryBuiltinNode {
-
- @Specialization
- static PByteArray doInt(int size,
- @Bind PythonLanguage language) {
- return PFactory.createByteArray(language, new byte[size]);
- }
-
- @Specialization(rewriteOn = OverflowException.class)
- static PByteArray doLong(long size,
- @Bind PythonLanguage language) throws OverflowException {
- return doInt(PInt.intValueExact(size), language);
- }
-
- @Specialization(replaces = "doLong")
- static PByteArray doLongOvf(long size,
- @Bind Node inliningTarget,
- @Bind PythonLanguage language,
- @Shared("raiseNode") @Cached PRaiseNode raiseNode) {
- try {
- return doInt(PInt.intValueExact(size), language);
- } catch (OverflowException e) {
- throw raiseNode.raise(inliningTarget, IndexError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, size);
- }
- }
-
- @Specialization(rewriteOn = OverflowException.class)
- static PByteArray doPInt(PInt size,
- @Bind PythonLanguage language) throws OverflowException {
- return doInt(size.intValueExact(), language);
- }
-
- @Specialization(replaces = "doPInt")
- static PByteArray doPIntOvf(PInt size,
- @Bind Node inliningTarget,
- @Bind PythonLanguage language,
- @Shared("raiseNode") @Cached PRaiseNode raiseNode) {
- try {
- return doInt(size.intValueExact(), language);
- } catch (OverflowException e) {
- throw raiseNode.raise(inliningTarget, IndexError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, size);
- }
+ @CApiBuiltin(ret = PyObjectRawPointer, args = {Py_ssize_t}, call = Ignored)
+ static long GraalPyPrivate_ByteArray_EmptyWithCapacity(long size) {
+ try {
+ Object result = PFactory.createByteArray(PythonLanguage.get(null), new byte[PInt.intValueExact(size)]);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (OverflowException e) {
+ throw PRaiseNode.raiseStatic(null, IndexError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, size);
}
}
- @CApiBuiltin(ret = Int, args = {PyObject}, call = CApiCallPath.Ignored)
- abstract static class GraalPyPrivate_Bytes_CheckEmbeddedNull extends CApiUnaryBuiltinNode {
-
- @Specialization
- static int doBytes(Object bytes,
- @Bind Node inliningTarget,
- @Cached GetBytesStorage getBytesStorage,
- @Cached GetItemScalarNode getItemScalarNode) {
- SequenceStorage sequenceStorage = getBytesStorage.execute(inliningTarget, bytes);
- int len = sequenceStorage.length();
- try {
- for (int i = 0; i < len; i++) {
- if (getItemScalarNode.executeInt(inliningTarget, sequenceStorage, i) == 0) {
- return -1;
- }
+ @CApiBuiltin(ret = Int, args = {PyObjectRawPointer}, call = CApiCallPath.Ignored)
+ static int GraalPyPrivate_Bytes_CheckEmbeddedNull(long bytesPtr) {
+ Object bytes = NativeToPythonNode.executeRawUncached(bytesPtr);
+ SequenceStorage sequenceStorage = GetBytesStorage.executeUncached(bytes);
+ int len = sequenceStorage.length();
+ try {
+ for (int i = 0; i < len; i++) {
+ if (GetItemScalarNode.executeIntUncached(sequenceStorage, i) == 0) {
+ return -1;
}
- } catch (UnexpectedResultException e) {
- throw CompilerDirectives.shouldNotReachHere("bytes object contains non-int value");
}
- return 0;
+ } catch (UnexpectedResultException e) {
+ throw CompilerDirectives.shouldNotReachHere("bytes object contains non-int value");
}
+ return 0;
}
- @CApiBuiltin(ret = CHAR_PTR, args = {PyObject}, call = Direct)
- abstract static class PyBytes_AsString extends CApiUnaryBuiltinNode {
- @Specialization
- static Object doBytes(PBytes bytes) {
+ @CApiBuiltin(ret = CHAR_PTR, args = {PyObjectRawPointer}, call = Direct)
+ static long PyBytes_AsString(long bytesPtr) {
+ Object obj = NativeToPythonNode.executeRawUncached(bytesPtr);
+ if (obj instanceof PBytes bytes) {
return PySequenceArrayWrapper.ensureNativeSequence(bytes);
}
-
- @Specialization
- static Object doNative(PythonAbstractNativeObject obj,
- @Bind Node inliningTarget,
- @Cached GetPythonObjectClassNode getClassNode,
- @Cached IsSubtypeNode isSubtypeNode,
- @Cached CStructAccess.GetElementPtrNode getArray,
- @Cached PRaiseNode raiseNode) {
- if (isSubtypeNode.execute(getClassNode.execute(inliningTarget, obj), PythonBuiltinClassType.PBytes)) {
- return getArray.getElementPtr(obj.getPtr(), CFields.PyBytesObject__ob_sval);
+ if (obj instanceof PythonAbstractNativeObject nativeObj) {
+ Object type = GetClassNode.executeUncached(nativeObj);
+ if (IsSubtypeNode.getUncached().execute(type, PythonBuiltinClassType.PBytes)) {
+ return getFieldPtr(nativeObj.getPtr(), CFields.PyBytesObject__ob_sval);
}
- return doError(obj, raiseNode);
- }
-
- @Fallback
- static Object doError(Object obj,
- @Bind Node inliningTarget) {
- throw PRaiseNode.raiseStatic(inliningTarget, PythonErrorType.TypeError, ErrorMessages.EXPECTED_S_P_FOUND, "bytes", obj);
}
+ throw PRaiseNode.raiseStatic(null, PythonErrorType.TypeError, ErrorMessages.EXPECTED_S_P_FOUND, "bytes", obj);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCEvalBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCEvalBuiltins.java
index 0a54aeaeaf..377c87dcc1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCEvalBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCEvalBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -45,22 +45,22 @@
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyFrameObjectBorrowed;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectBorrowed;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectConstPtr;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectRawPointer;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyThreadState;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi11BuiltinNode;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
-import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiNullaryBuiltinNode;
-import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode;
import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
import com.oracle.graal.python.builtins.objects.cell.PCell;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
-import com.oracle.graal.python.builtins.objects.cext.capi.PThreadState;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ToNativeBorrowedNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.code.CodeNodes;
import com.oracle.graal.python.builtins.objects.code.PCode;
@@ -86,133 +86,93 @@
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.TruffleLogger;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.strings.TruffleString;
public final class PythonCextCEvalBuiltins {
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(PythonCextCEvalBuiltins.class);
@CApiBuiltin(ret = PyThreadState, args = {}, acquireGil = false, call = Direct)
- abstract static class PyEval_SaveThread extends CApiNullaryBuiltinNode {
- private static final TruffleLogger LOGGER = CApiContext.getLogger(PyEval_SaveThread.class);
-
- @Specialization
- static Object save(@Cached GilNode gil,
- @Bind Node inliningTarget,
- @Bind PythonContext context) {
- Object threadState = PThreadState.getOrCreateNativeThreadState(context.getLanguage(inliningTarget), context);
- LOGGER.fine("C extension releases GIL");
- gil.release(context, true);
- return threadState;
- }
+ static long PyEval_SaveThread() {
+ PythonContext context = PythonContext.get(null);
+ long threadState = context.getThreadState(context.getLanguage()).getNativePointer();
+ assert threadState != PythonAbstractObject.UNINITIALIZED;
+ LOGGER.fine("C extension releases GIL");
+ GilNode.getUncached().release(context, true);
+ return threadState;
}
@CApiBuiltin(ret = Void, args = {PyThreadState}, acquireGil = false, call = Direct)
- abstract static class PyEval_RestoreThread extends CApiUnaryBuiltinNode {
- private static final TruffleLogger LOGGER = CApiContext.getLogger(PyEval_RestoreThread.class);
-
- @Specialization
- static Object restore(@SuppressWarnings("unused") Object ptr,
- @Bind Node inliningTarget,
- @Bind PythonContext context,
- @Cached GilNode gil) {
- /*
- * The thread state is not really used but fetching it checks if we are shutting down
- * and will handle that properly.
- */
- context.getThreadState(context.getLanguage(inliningTarget));
- LOGGER.fine("C extension acquires GIL");
- gil.acquire(context);
- return PNone.NO_VALUE;
- }
+ static void PyEval_RestoreThread(@SuppressWarnings("unused") long ptr) {
+ PythonContext context = PythonContext.get(null);
+ /*
+ * The thread state is not really used but fetching it checks if we are shutting down and
+ * will handle that properly.
+ */
+ context.getThreadState(PythonLanguage.get(null));
+ LOGGER.fine("C extension acquires GIL");
+ GilNode.getUncached().acquire(context, null);
}
@CApiBuiltin(ret = PyObjectBorrowed, args = {}, call = Direct)
- abstract static class PyEval_GetBuiltins extends CApiNullaryBuiltinNode {
- @Specialization
- Object release(
- @Cached GetDictIfExistsNode getDictNode) {
- PythonModule cext = getCore().getBuiltins();
- return getDictNode.execute(cext);
- }
+ static long PyEval_GetBuiltins() {
+ PythonModule cext = PythonContext.get(null).getBuiltins();
+ return ToNativeBorrowedNode.executeUncached(GetDictIfExistsNode.getUncached().execute(cext));
}
@CApiBuiltin(ret = PyFrameObjectBorrowed, args = {}, call = Direct)
- abstract static class PyEval_GetFrame extends CApiNullaryBuiltinNode {
- @Specialization
- Object getFrame(
- @Cached ReadFrameNode readFrameNode) {
- PFrame pFrame = readFrameNode.getCurrentPythonFrame(null);
- return pFrame != null ? pFrame : getNativeNull();
- }
+ static long PyEval_GetFrame() {
+ PFrame pFrame = ReadFrameNode.getUncached().getCurrentPythonFrame(null);
+ return pFrame != null ? ToNativeBorrowedNode.executeUncached(pFrame) : NULLPTR;
}
- @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, PyObject, PyObjectConstPtr, Int, PyObjectConstPtr, Int, PyObjectConstPtr, Int, PyObject, PyObject}, call = Ignored)
- abstract static class GraalPyPrivate_Eval_EvalCodeEx extends CApi11BuiltinNode {
- @Specialization
- static Object doGeneric(PCode code, PythonObject globals, Object locals,
- Object argumentArrayPtr, int argumentCount, Object kwsPtr, int kwsCount, Object defaultValueArrayPtr, int defaultValueCount,
- Object kwdefaultsWrapper, Object closureObj,
- @Bind Node inliningTarget,
- @Bind PythonLanguage language,
- @Cached PRaiseNode raiseNode,
- @Cached CStructAccess.ReadObjectNode readNode,
- @Cached PythonCextBuiltins.CastKwargsNode castKwargsNode,
- @Cached CastToTruffleStringNode castToStringNode,
- @Cached SequenceNodes.GetObjectArrayNode getObjectArrayNode,
- @Cached CodeNodes.GetCodeSignatureNode getSignatureNode,
- @Cached CodeNodes.GetCodeCallTargetNode getCallTargetNode,
- @Cached CreateArgumentsNode createArgumentsNode,
- @Cached CallDispatchers.SimpleIndirectInvokeNode invoke) {
- Object[] defaults = readNode.readPyObjectArray(defaultValueArrayPtr, defaultValueCount);
- if (!PGuards.isPNone(kwdefaultsWrapper) && !PGuards.isDict(kwdefaultsWrapper)) {
- throw raiseNode.raise(inliningTarget, SystemError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC);
- }
- PKeyword[] kwdefaults = castKwargsNode.execute(inliningTarget, kwdefaultsWrapper);
- PCell[] closure = null;
- if (closureObj != PNone.NO_VALUE) {
- // CPython also just accesses the object as tuple without further checks.
- closure = PCell.toCellArray(getObjectArrayNode.execute(inliningTarget, closureObj));
- }
- Object[] kws = readNode.readPyObjectArray(kwsPtr, kwsCount * 2);
-
- PKeyword[] keywords = PKeyword.create(kws.length / 2);
- for (int i = 0; i < kws.length / 2; i += 2) {
- TruffleString keywordName = castToStringNode.execute(inliningTarget, kws[i]);
- keywords[i] = new PKeyword(keywordName, kws[i + 1]);
- }
-
- // prepare Python frame arguments
- Object[] userArguments = readNode.readPyObjectArray(argumentArrayPtr, argumentCount);
- Signature signature = getSignatureNode.execute(inliningTarget, code);
- PFunction function = PFactory.createFunction(language, code.getName(), code, globals, closure);
- Object[] pArguments = createArgumentsNode.execute(inliningTarget, code, userArguments, keywords, signature, null, null, defaults, kwdefaults, false);
-
- // set custom locals
- if (!(locals instanceof PNone)) {
- PArguments.setSpecialArgument(pArguments, locals);
- }
- PArguments.setFunctionObject(pArguments, function);
- // TODO(fa): set builtins in globals
- // PythonModule builtins = getContext().getBuiltins();
- // setBuiltinsInGlobals(globals, setBuiltins, builtins, lib);
- PArguments.setGlobals(pArguments, globals);
+ @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer, PyObjectConstPtr, Int, PyObjectConstPtr, Int, PyObjectConstPtr, Int,
+ PyObjectRawPointer, PyObjectRawPointer}, call = Ignored)
+ static long GraalPyPrivate_Eval_EvalCodeEx(long codePtr, long globalsPtr, long localsPtr,
+ long argumentArrayPtr, int argumentCount, long kwsPtr, int kwsCount, long defaultValueArrayPtr, int defaultValueCount,
+ long kwdefaultsWrapperPtr, long closureObjPtr) {
+ PCode code = (PCode) NativeToPythonNode.executeRawUncached(codePtr);
+ PythonObject globals = (PythonObject) NativeToPythonNode.executeRawUncached(globalsPtr);
+ Object locals = NativeToPythonNode.executeRawUncached(localsPtr);
+ Object kwdefaultsWrapper = NativeToPythonNode.executeRawUncached(kwdefaultsWrapperPtr);
+ Object closureObj = NativeToPythonNode.executeRawUncached(closureObjPtr);
+ Object[] defaults = CStructAccess.ReadObjectNode.getUncached().readPyObjectArray(defaultValueArrayPtr, defaultValueCount);
+ if (!PGuards.isPNone(kwdefaultsWrapper) && !PGuards.isDict(kwdefaultsWrapper)) {
+ throw PRaiseNode.raiseStatic(null, SystemError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC);
+ }
+ PKeyword[] kwdefaults = PythonCextBuiltins.CastKwargsNode.executeUncached(kwdefaultsWrapper);
+ PCell[] closure = null;
+ if (closureObj != PNone.NO_VALUE) {
+ // CPython also just accesses the object as tuple without further checks.
+ closure = PCell.toCellArray(SequenceNodes.GetObjectArrayNode.executeUncached(closureObj));
+ }
+ Object[] kws = CStructAccess.ReadObjectNode.getUncached().readPyObjectArray(kwsPtr, kwsCount * 2);
+ PKeyword[] keywords = PKeyword.create(kws.length / 2);
+ for (int i = 0, j = 0; i < kws.length; i += 2, j++) {
+ TruffleString keywordName = CastToTruffleStringNode.castKnownStringUncached(kws[i]);
+ keywords[j] = new PKeyword(keywordName, kws[i + 1]);
+ }
- RootCallTarget rootCallTarget = getCallTargetNode.execute(inliningTarget, code);
- return invoke.execute(null, inliningTarget, rootCallTarget, pArguments);
+ Object[] userArguments = CStructAccess.ReadObjectNode.getUncached().readPyObjectArray(argumentArrayPtr, argumentCount);
+ Signature signature = CodeNodes.GetCodeSignatureNode.executeUncached(code);
+ PFunction function = PFactory.createFunction(PythonLanguage.get(null), code.getName(), code, globals, closure);
+ Object[] pArguments = CreateArgumentsNode.executeUncached(code, userArguments, keywords, signature, null, null, defaults, kwdefaults, false);
+ if (!(locals instanceof PNone)) {
+ PArguments.setSpecialArgument(pArguments, locals);
}
+ PArguments.setFunctionObject(pArguments, function);
+ // TODO(fa): set builtins in globals
+ // PythonModule builtins = getContext().getBuiltins();
+ // setBuiltinsInGlobals(globals, setBuiltins, builtins, lib);
+ PArguments.setGlobals(pArguments, globals);
+
+ RootCallTarget rootCallTarget = CodeNodes.GetCodeCallTargetNode.executeUncached(code);
+ Object result = CallDispatchers.SimpleIndirectInvokeNode.executeUncached(rootCallTarget, pArguments);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
}
@CApiBuiltin(ret = PyObjectBorrowed, args = {}, call = Direct)
- abstract static class PyEval_GetGlobals extends CApiNullaryBuiltinNode {
- @Specialization
- Object get(
- @Bind Node inliningTarget,
- @Cached PyEvalGetGlobals getGlobals) {
- PythonObject globals = getGlobals.execute(null, inliningTarget);
- return globals != null ? globals : getNativeNull();
- }
+ static long PyEval_GetGlobals() {
+ PythonObject globals = PyEvalGetGlobals.executeUncached(null);
+ return globals != null ? ToNativeBorrowedNode.executeUncached(globals) : NULLPTR;
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCapsuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCapsuleBuiltins.java
index a94eeeb275..3c1797fc5d 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCapsuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCapsuleBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, 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
@@ -47,8 +47,9 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_CAPSULE_DESTRUCTOR;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Pointer;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectRawPointer;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readByteArrayElement;
import static com.oracle.graal.python.nodes.ErrorMessages.CALLED_WITH_INCORRECT_NAME;
import static com.oracle.graal.python.nodes.ErrorMessages.CALLED_WITH_INVALID_PY_CAPSULE_OBJECT;
import static com.oracle.graal.python.nodes.ErrorMessages.CALLED_WITH_NULL_POINTER;
@@ -56,18 +57,19 @@
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
-import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiTernaryBuiltinNode;
-import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode;
+import com.oracle.graal.python.builtins.modules.cext.PythonCextCapsuleBuiltinsFactory.PyCapsuleNewNodeGen;
import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
-import com.oracle.graal.python.builtins.objects.capsule.PyCapsuleNameMatchesNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.StringLiterals;
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
import com.oracle.graal.python.nodes.statement.AbstractImportNode;
import com.oracle.graal.python.runtime.object.PFactory;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
@@ -75,74 +77,63 @@
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.strings.TruffleString;
public final class PythonCextCapsuleBuiltins {
- @CApiBuiltin(ret = PyObjectTransfer, args = {Pointer, ConstCharPtr, PY_CAPSULE_DESTRUCTOR}, call = Direct)
- abstract static class PyCapsule_New extends CApiTernaryBuiltinNode {
- @Specialization
- static Object doGeneric(Object pointer, Object namePtr, Object destructor,
- @Bind Node inliningTarget,
- @Cached PyCapsuleNewNode pyCapsuleNewNode) {
- return pyCapsuleNewNode.execute(inliningTarget, pointer, namePtr, destructor);
- }
+ @CApiBuiltin(ret = PyObjectRawPointer, args = {Pointer, ConstCharPtr, PY_CAPSULE_DESTRUCTOR}, call = Direct)
+ static long PyCapsule_New(long pointer, long namePtr, long destructor) {
+ PyCapsule capsule = PyCapsuleNewNode.executeUncached(pointer, namePtr, destructor);
+ return PythonToNativeNewRefNode.executeLongUncached(capsule);
}
@GenerateCached(false)
@GenerateInline
+ @GenerateUncached
public abstract static class PyCapsuleNewNode extends Node {
- public abstract PyCapsule execute(Node inliningTarget, Object pointer, Object name, Object destructor);
+ public abstract PyCapsule execute(Node inliningTarget, long pointer, long name, long destructor);
+
+ @TruffleBoundary
+ public static PyCapsule executeUncached(long pointer, long name, long destructor) {
+ return PyCapsuleNewNodeGen.getUncached().execute(null, pointer, name, destructor);
+ }
@Specialization
- static PyCapsule doGeneric(Node inliningTarget, Object pointer, Object namePtr, Object destructor,
- @CachedLibrary(limit = "1") InteropLibrary interopLibrary,
+ static PyCapsule doGeneric(Node inliningTarget, long pointer, long namePtr, long destructor,
@Bind PythonLanguage language,
@Cached PRaiseNode raiseNode) {
- if (interopLibrary.isNull(pointer)) {
+ if (pointer == NULLPTR) {
throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT);
}
- PyCapsule capsule = PFactory.createCapsuleNativeName(language, pointer, interopLibrary.isNull(namePtr) ? null : namePtr);
- if (!interopLibrary.isNull(destructor)) {
+ PyCapsule capsule = PFactory.createCapsuleNativeName(language, pointer, namePtr);
+ if (destructor != NULLPTR) {
capsule.registerDestructor(destructor);
}
return capsule;
}
}
- @CApiBuiltin(ret = Int, args = {PyObject, ConstCharPtr}, call = Direct)
- abstract static class PyCapsule_IsValid extends CApiBinaryBuiltinNode {
- @Specialization
- static int doCapsule(PyCapsule o, Object namePtr,
- @Bind Node inliningTarget,
- @Cached PyCapsuleNameMatchesNode nameMatchesNode) {
- if (o.getPointer() == null) {
- return 0;
- }
- if (!nameMatchesNode.execute(inliningTarget, namePtr, o.getNamePtr())) {
- return 0;
- }
- return 1;
+ @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, ConstCharPtr}, call = Direct)
+ static int PyCapsule_IsValid(long oPtr, long namePtr) {
+ Object obj = NativeToPythonNode.executeRawUncached(oPtr);
+ if (!(obj instanceof PyCapsule capsule)) {
+ return 0;
}
-
- @Fallback
- static Object doError(@SuppressWarnings("unused") Object o, @SuppressWarnings("unused") Object name) {
+ if (capsule.getPointer() == NULLPTR) {
return 0;
}
+ if (!capsuleNameMatches(namePtr, capsule.getNamePtr())) {
+ return 0;
+ }
+ return 1;
}
- @CApiBuiltin(ret = Pointer, args = {PyObject, ConstCharPtr}, call = Direct)
- abstract static class PyCapsule_GetPointer extends CApiBinaryBuiltinNode {
- @Specialization
- static Object doCapsule(Object o, Object name,
- @Bind Node inliningTarget,
- @Cached PyCapsuleGetPointerNode pyCapsuleGetPointerNode) {
- return pyCapsuleGetPointerNode.execute(inliningTarget, o, name);
- }
+ @CApiBuiltin(ret = Pointer, args = {PyObjectRawPointer, ConstCharPtr}, call = Direct)
+ static long PyCapsule_GetPointer(long oPtr, long namePtr) {
+ Object capsule = NativeToPythonNode.executeRawUncached(oPtr);
+ return PyCapsuleGetPointerNode.executeUncached(capsule, namePtr);
}
@GenerateCached(false)
@@ -150,218 +141,154 @@ static Object doCapsule(Object o, Object name,
@GenerateUncached
public abstract static class PyCapsuleGetPointerNode extends Node {
- public abstract Object execute(Node inliningTarget, Object capsule, Object name);
+ public abstract long execute(Node inliningTarget, Object capsule, long name);
+
+ public static PyCapsuleGetPointerNode getUncached() {
+ return PythonCextCapsuleBuiltinsFactory.PyCapsuleGetPointerNodeGen.getUncached();
+ }
+
+ @TruffleBoundary
+ public static long executeUncached(Object capsuleObj, long name) {
+ return getUncached().execute(null, capsuleObj, name);
+ }
@Specialization
- static Object doCapsule(Node inliningTarget, PyCapsule o, Object name,
- @Cached PyCapsuleNameMatchesNode nameMatchesNode,
+ static long doCapsule(Node inliningTarget, PyCapsule o, long name,
@Cached PRaiseNode raiseNode) {
- if (o.getPointer() == null) {
+ if (o.getPointer() == NULLPTR) {
throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_GetPointer");
}
- if (!nameMatchesNode.execute(inliningTarget, name, o.getNamePtr())) {
+ if (!capsuleNameMatches(name, o.getNamePtr())) {
throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INCORRECT_NAME, "PyCapsule_GetPointer");
}
return o.getPointer();
}
@Fallback
- static Object doError(Node inliningTarget, @SuppressWarnings("unused") Object o, @SuppressWarnings("unused") Object name) {
+ static long doError(Node inliningTarget, @SuppressWarnings("unused") Object o, @SuppressWarnings("unused") long name) {
throw PRaiseNode.raiseStatic(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_GetPointer");
}
}
- @CApiBuiltin(ret = ConstCharPtr, args = {PyObject}, call = Direct)
- abstract static class PyCapsule_GetName extends CApiUnaryBuiltinNode {
+ @CApiBuiltin(ret = ConstCharPtr, args = {PyObjectRawPointer}, call = Direct)
+ static long PyCapsule_GetName(long oPtr) {
+ PyCapsule capsule = expectCapsule(oPtr, "PyCapsule_GetName");
+ return capsule.getNamePtr();
+ }
- @Specialization
- Object get(PyCapsule o,
- @Bind Node inliningTarget,
- @Cached PRaiseNode raiseNode) {
- if (o.getPointer() == null) {
- throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_GetName");
- }
- return o.getNamePtr() == null ? getNULL() : o.getNamePtr();
- }
+ @CApiBuiltin(ret = PY_CAPSULE_DESTRUCTOR, args = {PyObjectRawPointer}, call = Direct)
+ static long PyCapsule_GetDestructor(long oPtr) {
+ PyCapsule capsule = expectCapsule(oPtr, "PyCapsule_GetDestructor");
+ return capsule.getDestructor();
+ }
- @Fallback
- static Object doit(@SuppressWarnings("unused") Object o,
- @Bind Node inliningTarget) {
- throw PRaiseNode.raiseStatic(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_GetName");
- }
+ @CApiBuiltin(ret = Pointer, args = {PyObjectRawPointer}, call = Direct)
+ static long PyCapsule_GetContext(long oPtr) {
+ PyCapsule capsule = expectCapsule(oPtr, "PyCapsule_GetContext");
+ return capsule.getContext();
}
- @CApiBuiltin(ret = PY_CAPSULE_DESTRUCTOR, args = {PyObject}, call = Direct)
- abstract static class PyCapsule_GetDestructor extends CApiUnaryBuiltinNode {
- @Specialization
- Object doCapsule(PyCapsule o,
- @Bind Node inliningTarget,
- @Cached PRaiseNode raiseNode) {
- if (o.getPointer() == null) {
- throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_GetDestructor");
- }
- if (o.getDestructor() == null) {
- return getNULL();
- }
- return o.getDestructor();
+ @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, Pointer}, call = Direct)
+ static int PyCapsule_SetPointer(long oPtr, long pointer) {
+ if (pointer == NULLPTR) {
+ throw PRaiseNode.raiseStatic(null, ValueError, CALLED_WITH_NULL_POINTER, "PyCapsule_SetPointer");
}
+ PyCapsule capsule = expectCapsule(oPtr, "PyCapsule_SetPointer");
+ capsule.setPointer(pointer);
+ return 0;
+ }
- @Fallback
- static Object doError(@SuppressWarnings("unused") Object o,
- @Bind Node inliningTarget) {
- throw PRaiseNode.raiseStatic(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_GetPointer");
- }
+ @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, ConstCharPtr}, call = Direct)
+ static int PyCapsule_SetName(long oPtr, long namePtr) {
+ PyCapsule capsule = expectCapsule(oPtr, "PyCapsule_SetName");
+ capsule.setNamePtr(namePtr);
+ return 0;
}
- @CApiBuiltin(ret = Pointer, args = {PyObject}, call = Direct)
- abstract static class PyCapsule_GetContext extends CApiUnaryBuiltinNode {
- @Specialization
- Object doCapsule(PyCapsule o,
- @Bind Node inliningTarget,
- @Cached PRaiseNode raiseNode) {
- if (o.getPointer() == null) {
- throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_GetContext");
- }
- if (o.getContext() == null) {
- return getNULL();
- }
- return o.getContext();
- }
+ @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, PY_CAPSULE_DESTRUCTOR}, call = Direct)
+ static int PyCapsule_SetDestructor(long oPtr, long destructor) {
+ PyCapsule capsule = expectCapsule(oPtr, "PyCapsule_SetDestructor");
+ capsule.registerDestructor(destructor);
+ return 0;
+ }
- @Fallback
- static Object doError(@SuppressWarnings("unused") Object o,
- @Bind Node inliningTarget) {
- throw PRaiseNode.raiseStatic(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_GetPointer");
- }
+ @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, Pointer}, call = Direct)
+ static int PyCapsule_SetContext(long oPtr, long context) {
+ PyCapsule capsule = expectCapsule(oPtr, "PyCapsule_SetContext");
+ capsule.setContext(context);
+ return 0;
}
- @CApiBuiltin(ret = Int, args = {PyObject, Pointer}, call = Direct)
- abstract static class PyCapsule_SetPointer extends CApiBinaryBuiltinNode {
- @Specialization
- static int doCapsule(PyCapsule o, Object pointer,
- @Bind Node inliningTarget,
- @CachedLibrary(limit = "2") InteropLibrary interopLibrary,
- @Cached PRaiseNode raiseNode) {
- if (interopLibrary.isNull(pointer)) {
- throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_NULL_POINTER, "PyCapsule_SetPointer");
+ @CApiBuiltin(ret = Pointer, args = {ConstCharPtr, Int}, call = Direct)
+ static long PyCapsule_Import(long namePtr, @SuppressWarnings("unused") int noBlock) {
+ TruffleString name = FromCharPointerNode.executeUncached(namePtr, true);
+ TruffleString trace = name;
+ Object object = null;
+ while (trace != null) {
+ int traceLen = trace.codePointLengthUncached(TS_ENCODING);
+ int dotIdx = trace.indexOfStringUncached(StringLiterals.T_DOT, 0, traceLen, TS_ENCODING);
+ TruffleString dot = null;
+ if (dotIdx >= 0) {
+ dot = trace.substringUncached(dotIdx + 1, traceLen - dotIdx - 1, TS_ENCODING, false);
+ trace = trace.substringUncached(0, dotIdx, TS_ENCODING, false);
}
-
- if (o.getPointer() == null) {
- throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_SetPointer");
+ if (object == null) {
+ // noBlock has no effect anymore since 3.3
+ object = AbstractImportNode.importModuleBoundary(trace);
+ } else {
+ object = ReadAttributeFromObjectNode.getUncached().execute(object, trace);
}
-
- o.setPointer(pointer);
- return 0;
+ trace = dot;
}
- @Fallback
- static Object doError(@SuppressWarnings("unused") Object o, @SuppressWarnings("unused") Object name,
- @Bind Node inliningTarget) {
- throw PRaiseNode.raiseStatic(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_SetPointer");
+ /* compare attribute name to module.name by hand */
+ PyCapsule capsule = object instanceof PyCapsule ? (PyCapsule) object : null;
+ if (capsule != null && capsule.getPointer() != NULLPTR && capsuleNameMatches(namePtr, capsule.getNamePtr())) {
+ return capsule.getPointer();
}
+ throw PRaiseNode.raiseStatic(null, AttributeError, PY_CAPSULE_IMPORT_S_IS_NOT_VALID, name);
}
- @CApiBuiltin(ret = Int, args = {PyObject, ConstCharPtr}, call = Direct)
- abstract static class PyCapsule_SetName extends CApiBinaryBuiltinNode {
- @Specialization
- static int set(PyCapsule o, Object namePtr,
- @Bind Node inliningTarget,
- @CachedLibrary(limit = "1") InteropLibrary lib,
- @Cached PRaiseNode raiseNode) {
- if (o.getPointer() == null) {
- throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_SetName");
- }
- o.setNamePtr(lib.isNull(namePtr) ? null : namePtr);
- return 0;
+ /**
+ * Compares two names according to the semantics of PyCapsule's {@code name_matches} function
+ * (see C code snippet below). The names must be native pointers (or {@code NULLPTR}).
+ *
+ *
+ * static int
+ * name_matches(const char *name1, const char *name2) {
+ * // if either is NULL
+ * if (!name1 || !name2) {
+ * // they're only the same if they're both NULL.
+ * return name1 == name2;
+ * }
+ * return !strcmp(name1, name2);
+ * }
+ *
- * static int
- * name_matches(const char *name1, const char *name2) {
- * // if either is NULL
- * if (!name1 || !name2) {
- * // they're only the same if they're both NULL.
- * return name1 == name2;
- * }
- * return !strcmp(name1, name2);
- * }
- *
- */
-@GenerateUncached
-@GenerateInline
-@GenerateCached(false)
-public abstract class PyCapsuleNameMatchesNode extends Node {
- public abstract boolean execute(Node inliningTarget, Object name1, Object name2);
-
- @Specialization
- static boolean compare(Node inliningTarget, Object name1, Object name2,
- @CachedLibrary(limit = "2") InteropLibrary lib,
- @Cached ReadByteNode readByteNode) {
- try {
- if (name1 != null && lib.isNull(name1)) {
- name1 = null;
- }
- if (name2 != null && lib.isNull(name2)) {
- name2 = null;
- }
- if (name1 == null || name2 == null) {
- return name1 == name2;
- }
- if (lib.isPointer(name1) && lib.isPointer(name2) && lib.asPointer(name1) == lib.asPointer(name2)) {
- return true;
- }
- for (int i = 0;; i++) {
- byte b1 = readByteNode.execute(inliningTarget, name1, i);
- byte b2 = readByteNode.execute(inliningTarget, name2, i);
- if (b1 != b2) {
- return false;
- }
- if (b1 == 0) {
- return true;
- }
- }
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
-
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- abstract static class ReadByteNode extends Node {
- public abstract byte execute(Node inliningTarget, Object ptr, int i);
-
- @Specialization
- static byte doManaged(CArrayWrappers.CByteArrayWrapper wrapper, int i) {
- byte[] bytes = wrapper.getByteArray();
- if (i < bytes.length) {
- return bytes[i];
- }
- return 0;
- }
-
- @Fallback
- static byte doNative(Object ptr, int i,
- @Cached(inline = false) CStructAccess.ReadByteNode readByteNode) {
- return readByteNode.readArrayElement(ptr, i);
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonAbstractNativeObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonAbstractNativeObject.java
index 72408f81f4..748d49a580 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonAbstractNativeObject.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonAbstractNativeObject.java
@@ -42,8 +42,6 @@
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_name;
-import java.util.Objects;
-
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAcquireLibrary;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
@@ -58,7 +56,6 @@
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
import com.oracle.graal.python.runtime.GilNode;
-import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -78,16 +75,17 @@
import com.oracle.truffle.api.profiles.InlinedExactClassProfile;
import com.oracle.truffle.api.utilities.TriState;
+/**
+ * A simple wrapper around objects created through the Python C API that can be cast to PyObject*.
+ */
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(PythonBufferAcquireLibrary.class)
public final class PythonAbstractNativeObject extends PythonAbstractObject implements PythonNativeObject, PythonNativeClass {
/**
- * A reference to the native object. This usually is a pointer object (i.e. responds to
- * {@link InteropLibrary#isPointer(Object)} with {@code true}) but can also be something the
- * emulates native memory.
+ * A pointer to the native object ({@code PyObject *}).
*/
- public final Object object;
+ public final long pointer;
public TpSlots slots;
public NativeObjectReference ref;
@@ -102,13 +100,12 @@ public final class PythonAbstractNativeObject extends PythonAbstractObject imple
*/
private Object[] replicatedNativeReferences;
- public PythonAbstractNativeObject(Object object) {
+ public PythonAbstractNativeObject(long pointer) {
// GR-50245
// Fails in
// graalpython/com.oracle.graal.python.hpy.test/src/hpytest/test_slots_legacy.py::TestCustomLegacySlotsFeatures::test_legacy_slots_getsets[hybrid]
- // assert !(object instanceof Number || object instanceof PythonNativeWrapper || object
- // instanceof String || object instanceof TruffleString);
- this.object = object;
+ assert pointer != UNINITIALIZED;
+ this.pointer = pointer;
}
@Override
@@ -128,15 +125,15 @@ public Object[] getReplicatedNativeReferences() {
}
@Override
- public Object getPtr() {
- return object;
+ public long getPtr() {
+ return pointer;
}
@Override
public int hashCode() {
CompilerAsserts.neverPartOfCompilation();
// this is important for the default '__hash__' implementation
- return Objects.hashCode(object);
+ return Long.hashCode(pointer);
}
@Ignore
@@ -149,27 +146,23 @@ public boolean equals(Object obj) {
return false;
}
PythonAbstractNativeObject other = (PythonAbstractNativeObject) obj;
- return Objects.equals(object, other.object);
+ return pointer == other.pointer;
}
@TruffleBoundary
public String toStringWithContext() {
- return "PythonAbstractNativeObject(" + PythonUtils.formatPointer(object) + ')';
+ return toString();
}
@Override
public String toString() {
CompilerAsserts.neverPartOfCompilation();
- return "PythonAbstractNativeObject(" + object + ')';
+ return String.format("PythonAbstractNativeObject(0x%x)", pointer);
}
@ExportMessage
- int identityHashCode(@CachedLibrary("this.object") InteropLibrary lib) throws UnsupportedMessageException {
- if (lib.isPointer(object)) {
- return Long.hashCode(lib.asPointer(object));
- } else {
- return lib.identityHashCode(object);
- }
+ int identityHashCode() {
+ return Long.hashCode(pointer);
}
@ExportMessage
@@ -177,31 +170,12 @@ boolean isIdentical(Object other, InteropLibrary otherInterop,
@Bind Node inliningTarget,
@Cached InlinedExactClassProfile otherProfile,
@Exclusive @CachedLibrary(limit = "1") InteropLibrary thisLib,
- @Exclusive @CachedLibrary(limit = "3") InteropLibrary lib1,
- @Exclusive @CachedLibrary(limit = "3") InteropLibrary lib2,
@Exclusive @Cached GilNode gil) {
boolean mustRelease = gil.acquire();
try {
Object profiled = otherProfile.profile(inliningTarget, other);
- if (profiled instanceof PythonAbstractNativeObject) {
- Object otherPtr = ((PythonAbstractNativeObject) other).getPtr();
- if (lib1.isPointer(getPtr())) {
- if (lib2.isPointer(otherPtr)) {
- try {
- return lib1.asPointer(getPtr()) == lib2.asPointer(otherPtr);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- } else {
- return false;
- }
- } else {
- if (lib2.isPointer(otherPtr)) {
- return false;
- } else {
- return lib1.isIdentical(getPtr(), otherPtr, lib2);
- }
- }
+ if (profiled instanceof PythonAbstractNativeObject otherNativeObject) {
+ return pointer == otherNativeObject.pointer;
}
return otherInterop.isIdentical(profiled, this, thisLib);
} finally {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeClass.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeClass.java
index 0684f8ada7..1e2abae762 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeClass.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeClass.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -50,7 +50,7 @@
*/
public interface PythonNativeClass extends PythonAbstractClass, PythonNativeObject {
- Object getPtr();
+ long getPtr();
static boolean isInstance(Object object) {
return object instanceof PythonAbstractNativeObject;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeObject.java
index a563e98f44..9748d7c494 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeObject.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeObject.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -45,7 +45,7 @@
*/
public interface PythonNativeObject {
- Object getPtr();
+ long getPtr();
static boolean isInstance(Object object) {
return object instanceof PythonAbstractNativeObject;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeVoidPtr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeVoidPtr.java
deleted file mode 100644
index 64b8deee6c..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/PythonNativeVoidPtr.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2018, 2022, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-// skip GIL
-package com.oracle.graal.python.builtins.objects.cext;
-
-import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
-import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerAsserts;
-
-/**
- * Represents the value of a native pointer as Python int.
- * Such objects are created using C API function {@code PyLong_FromVoidPtr} and semantics are best
- * explained by looking at how CPython constructs the value:
- * {@code PyLong_FromUnsignedLong((unsigned long)(uintptr_t)p)}. CPython casts the {@code (void *)}
- * to an integer and this will be the value for the Python int. In our case, to get a numeric
- * representation of a pointer, we would need to send a to-native message. However, we try to avoid
- * this eager transformation using this wrapper.
- */
-public class PythonNativeVoidPtr extends PythonAbstractObject {
- private final Object object;
- private final long nativePointerValue;
- private final boolean hasNativePointer;
-
- public PythonNativeVoidPtr(Object object) {
- this.object = object;
- this.nativePointerValue = 0;
- this.hasNativePointer = false;
- }
-
- public PythonNativeVoidPtr(Object object, long nativePointerValue) {
- this.object = object;
- this.nativePointerValue = nativePointerValue;
- this.hasNativePointer = true;
- }
-
- public Object getPointerObject() {
- return object;
- }
-
- public long getNativePointer() {
- return nativePointerValue;
- }
-
- public boolean isNativePointer() {
- return hasNativePointer;
- }
-
- @Override
- public int compareTo(Object o) {
- return 0;
- }
-
- @Override
- public String toString() {
- CompilerAsserts.neverPartOfCompilation();
- return PythonUtils.formatJString("PythonNativeVoidPtr(%s)", object);
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
index 22ae518194..7dee2c1dee 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
@@ -41,10 +41,13 @@
package com.oracle.graal.python.builtins.objects.cext.capi;
import static com.oracle.graal.python.PythonLanguage.CONTEXT_INSENSITIVE_SINGLETONS;
-import static com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper.IMMORTAL_REFCNT;
+import static com.oracle.graal.python.builtins.objects.PythonAbstractObject.UNINITIALIZED;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.pollReferenceQueue;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
+import static com.oracle.graal.python.builtins.objects.object.PythonObject.IMMORTAL_REFCNT;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___FILE__;
-import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE;
import static com.oracle.graal.python.nodes.StringLiterals.T_DASH;
import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING;
import static com.oracle.graal.python.nodes.StringLiterals.T_UNDERSCORE;
@@ -52,7 +55,7 @@
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
import java.io.IOException;
-import java.io.PrintStream;
+import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -73,7 +76,6 @@
import org.graalvm.shadowed.com.ibm.icu.text.StringPrepParseException;
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.annotations.PythonOS;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.modules.cext.PythonCApiAssertions;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltinRegistry;
@@ -81,13 +83,14 @@
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.PyObjectCheckFunctionResultNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.FirstToNativeNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandleContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.EnsureExecutableNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ApiInitException;
import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ImportException;
@@ -95,23 +98,31 @@
import com.oracle.graal.python.builtins.objects.cext.copying.NativeLibraryLocator;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.FreeNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.frame.PFrame;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.builtins.objects.module.PythonModule;
+import com.oracle.graal.python.builtins.objects.object.PythonObject;
import com.oracle.graal.python.builtins.objects.str.PString;
import com.oracle.graal.python.builtins.objects.str.StringNodes;
import com.oracle.graal.python.builtins.objects.str.StringUtils;
import com.oracle.graal.python.builtins.objects.thread.PLock;
+import com.oracle.graal.python.runtime.nativeaccess.NativeContext;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
+import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary;
+import com.oracle.graal.python.runtime.nativeaccess.NativeLibraryLoadException;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
+import com.oracle.graal.python.runtime.nativeaccess.NativeSignature;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.nodes.statement.AbstractImportNode;
import com.oracle.graal.python.runtime.GilNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PosixConstants;
import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.PythonContext.CApiState;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.graal.python.runtime.exception.PException;
@@ -120,7 +131,6 @@
import com.oracle.graal.python.util.PythonSystemThreadTask;
import com.oracle.graal.python.util.PythonUtils;
import com.oracle.graal.python.util.SuppressFBWarnings;
-import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -133,23 +143,14 @@
import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.TruffleSafepoint;
import com.oracle.truffle.api.exception.AbstractTruffleException;
-import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.interop.UnsupportedTypeException;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.ExplodeLoop.LoopExplosionKind;
import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.source.Source.SourceBuilder;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.strings.TruffleString.CodeRange;
-import com.oracle.truffle.nfi.api.SignatureLibrary;
import sun.misc.Unsafe;
@@ -159,22 +160,23 @@ public final class CApiContext extends CExtContext {
public static final String LOGGER_CAPI_NAME = "capi";
- /**
- * NFI source for Python module init functions (i.e. {@code "PyInit_modname"}).
- */
- private static final Source MODINIT_SRC = Source.newBuilder(J_NFI_LANGUAGE, "():POINTER", "modinit").build();
+ private static final CApiTiming TIMING_INVOKE_MODULE_INIT = CApiTiming.create(true, "invokeModuleInit");
+ private static final CApiTiming TIMING_INVOKE_CAPI_INIT = CApiTiming.create(true, "invokeCApiInit");
+ private static final CApiTiming TIMING_INVOKE_GET_FINALIZE_CAPI_POINTER = CApiTiming.create(true, "invokeGetFinalizeCApiPointer");
+ private static final TruffleString C_API_UNSUPPORTED = tsLiteral(
+ "The C API is unsupported on this JDK and/or platform, either because JEP 454 is not supported here or native access was expressly forbidden.");
private static final TruffleLogger LOGGER = PythonLanguage.getLogger(LOGGER_CAPI_NAME);
public static final TruffleLogger GC_LOGGER = PythonLanguage.getLogger(CApiContext.LOGGER_CAPI_NAME + ".gc");
- /** Native wrappers for context-insensitive singletons like {@link PNone#NONE}. */
- @CompilationFinal(dimensions = 1) private final PythonAbstractObjectNativeWrapper[] singletonNativePtrs;
+ /** Native pointers for context-insensitive singletons like {@link PNone#NONE}. */
+ @CompilationFinal(dimensions = 1) private final long[] singletonNativePtrs;
/**
* Pointer to the native {@code GCState GC state}. This corresponds to CPython's
* {@code PyInterpreterState.gc}.
*/
- private Object gcState;
+ private long gcState;
/** Same as {@code import.c: extensions} but we don't keep a PDict; just a bare Java HashMap. */
private final HashMap, PythonModule> extensions = new HashMap<>(4);
@@ -183,13 +185,13 @@ public final class CApiContext extends CExtContext {
private final ConcurrentWeakSet pstringInterningCache = new ConcurrentWeakSet<>();
private final ArrayList modulesByIndex = new ArrayList<>(0);
- public final HashMap locks = new HashMap<>();
+ public final ConcurrentHashMap locks = new ConcurrentHashMap<>();
public final AtomicLong lockId = new AtomicLong();
/**
* Thread local storage for PyThread_tss_* APIs
*/
- private final ConcurrentHashMap> tssStorage = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap> tssStorage = new ConcurrentHashMap<>();
/**
* Next key that will be allocated byt PyThread_tss_create
*/
@@ -202,15 +204,19 @@ public final class CApiContext extends CExtContext {
* Same as {@link #nativeSymbolCache} if there is only one context per JVM (i.e. just one engine
* in single-context mode). Will be {@code null} in case of multiple contexts.
*/
- @CompilationFinal(dimensions = 1) private static Object[] nativeSymbolCacheSingleContext;
+ @CompilationFinal(dimensions = 1) private static NativeFunctionPointer[] nativeSymbolCacheSingleContext;
private static boolean nativeSymbolCacheSingleContextUsed;
/**
* A private (i.e. per-context) cache of C API symbols (usually helper functions).
*/
- private final Object[] nativeSymbolCache;
+ private final NativeFunctionPointer[] nativeSymbolCache;
+
+ public static boolean isSpecialSingleton(Object delegate) {
+ return getSingletonNativeWrapperIdx(delegate) != -1;
+ }
- private record ClosureInfo(Object closure, Object delegate, Object executable, long pointer) {
+ private record ClosureInfo(Object delegate, Object executable, long pointer) {
}
/**
@@ -290,7 +296,7 @@ public TruffleString getInitFunctionName() {
* CPython, those are usually statically allocated (or at least immortal) and once hand out a
* pointer for a {@code PyMethodDef}, we need to ensure that it stays valid until the end.
*/
- private final HashMap methodDefinitions = new HashMap<>(4);
+ private final HashMap methodDefinitions = new HashMap<>(4);
/**
* This list holds a strong reference to all loaded extension libraries to keep the library
@@ -316,9 +322,9 @@ public static TruffleLogger getLogger(Class> clazz) {
return PythonLanguage.getLogger(LOGGER_CAPI_NAME + "." + clazz.getSimpleName());
}
- public CApiContext(PythonContext context, Object library, NativeLibraryLocator locator) {
+ public CApiContext(PythonContext context, NativeLibrary library, NativeLibraryLocator locator) {
super(context, library, locator.getCapiLibrary());
- this.nativeSymbolCache = new Object[NativeCAPISymbol.values().length];
+ this.nativeSymbolCache = new NativeFunctionPointer[NativeCAPISymbol.values().length];
this.nativeLibraryLocator = locator;
/*
@@ -350,21 +356,9 @@ public CApiContext(PythonContext context, Object library, NativeLibraryLocator l
CApiContext.nativeSymbolCacheSingleContextUsed = true;
}
- // initialize singleton native wrappers
- singletonNativePtrs = new PythonAbstractObjectNativeWrapper[CONTEXT_INSENSITIVE_SINGLETONS.length];
- // Other threads must see the nativeWrapper fully initialized once it becomes non-null
- for (int i = 0; i < singletonNativePtrs.length; i++) {
- assert CApiGuards.isSpecialSingleton(CONTEXT_INSENSITIVE_SINGLETONS[i]);
- /*
- * Note: this does intentionally not use 'PythonObjectNativeWrapper.wrap' because the
- * wrapper must not be reachable from the Python object since the singletons are shared.
- */
- singletonNativePtrs[i] = new PythonObjectNativeWrapper(CONTEXT_INSENSITIVE_SINGLETONS[i]);
- }
-
- // initialize Py_True and Py_False
- context.getTrue().setNativeWrapper(PrimitiveNativeWrapper.createBool(true));
- context.getFalse().setNativeWrapper(PrimitiveNativeWrapper.createBool(false));
+ // initialize singleton native pointers array
+ singletonNativePtrs = new long[CONTEXT_INSENSITIVE_SINGLETONS.length];
+ Arrays.fill(singletonNativePtrs, UNINITIALIZED);
this.gcTask = new BackgroundGCTask(context);
}
@@ -406,17 +400,17 @@ public long nextTssKey() {
}
@TruffleBoundary
- public Object tssGet(long key) {
- ThreadLocal local = tssStorage.get(key);
+ public long tssGet(long key) {
+ ThreadLocal local = tssStorage.get(key);
if (local != null) {
return local.get();
}
- return null;
+ return NULLPTR;
}
@TruffleBoundary
- public void tssSet(long key, Object object) {
- tssStorage.computeIfAbsent(key, (k) -> new ThreadLocal<>()).set(object);
+ public void tssSet(long key, long ptr) {
+ tssStorage.computeIfAbsent(key, (k) -> new ThreadLocal<>()).set(ptr);
}
@TruffleBoundary
@@ -434,12 +428,35 @@ static int getSingletonNativeWrapperIdx(Object obj) {
return -1;
}
- public PythonAbstractObjectNativeWrapper getSingletonNativeWrapper(PythonAbstractObject obj) {
+ public long getNonePtr() {
+ return getSingletonNativeWrapper(PNone.NONE);
+ }
+
+ public long getSingletonNativeWrapper(PythonAbstractObject obj) {
int singletonNativePtrIdx = CApiContext.getSingletonNativeWrapperIdx(obj);
if (singletonNativePtrIdx != -1) {
- return singletonNativePtrs[singletonNativePtrIdx];
+ long singletonNativePtr = singletonNativePtrs[singletonNativePtrIdx];
+ if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, singletonNativePtr == UNINITIALIZED)) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ initializeSingletonNativePtrs();
+ singletonNativePtr = singletonNativePtrs[singletonNativePtrIdx];
+ }
+ return singletonNativePtr;
+ }
+ return 0;
+ }
+
+ private void initializeSingletonNativePtrs() {
+ CompilerAsserts.neverPartOfCompilation();
+ assert getContext().getCApiState() == CApiState.INITIALIZING || getContext().getCApiState() == CApiState.INITIALIZED;
+ for (int i = 0; i < singletonNativePtrs.length; i++) {
+ assert isSpecialSingleton(CONTEXT_INSENSITIVE_SINGLETONS[i]);
+ assert !PythonToNativeInternalNode.mapsToNull(CONTEXT_INSENSITIVE_SINGLETONS[i]);
+ assert singletonNativePtrs[i] == UNINITIALIZED;
+ singletonNativePtrs[i] = FirstToNativeNode.executeUncached(CONTEXT_INSENSITIVE_SINGLETONS[i], IMMORTAL_REFCNT);
+ assert singletonNativePtrs[i] != NULLPTR;
+ assert singletonNativePtrs[i] != UNINITIALIZED;
}
- return null;
}
/**
@@ -452,40 +469,36 @@ private void freeSingletonNativeWrappers(HandleContext handleContext) {
// TODO(fa): this should not require the GIL (GR-51314)
assert getContext().ownsGil();
for (int i = 0; i < singletonNativePtrs.length; i++) {
- PythonAbstractObjectNativeWrapper singletonNativeWrapper = singletonNativePtrs[i];
- singletonNativePtrs[i] = null;
- assert singletonNativeWrapper != null;
- assert getSingletonNativeWrapperIdx(singletonNativeWrapper.getDelegate()) != -1;
- assert !singletonNativeWrapper.isNative() || singletonNativeWrapper.getRefCount() == IMMORTAL_REFCNT;
- if (singletonNativeWrapper.ref != null) {
- CApiTransitions.nativeStubLookupRemove(handleContext, singletonNativeWrapper.ref);
+ long pointer = singletonNativePtrs[i];
+ assert pointer == PythonObject.UNINITIALIZED || CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(pointer)) == IMMORTAL_REFCNT;
+ singletonNativePtrs[i] = PythonObject.NATIVE_POINTER_FREED;
+ // It may be that the singleton was never used in native and there is nothing to free.
+ if (pointer != PythonObject.UNINITIALIZED) {
+ assert HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ int handleTableIndex = readIntField(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
+ assert CApiTransitions.nativeStubLookupGet(handleContext, pointer, handleTableIndex) instanceof PythonAbstractObject : "immortal objects should not have a weak ref";
+ CApiTransitions.nativeStubLookupRemove(handleContext, handleTableIndex);
+ CApiTransitions.releaseNativeWrapper(pointer);
}
- CApiTransitions.releaseNativeWrapperUncached(singletonNativeWrapper);
}
}
- public PrimitiveNativeWrapper getCachedBooleanPrimitiveNativeWrapper(boolean b) {
- PythonAbstractObjectNativeWrapper wrapper = b ? getContext().getTrue().getNativeWrapper() : getContext().getFalse().getNativeWrapper();
- assert wrapper.getRefCount() > 0;
- return (PrimitiveNativeWrapper) wrapper;
- }
-
/**
* Allocates the {@code GCState} which needs to happen very early in the C API initialization
* phase. Very early means it needs to happen before the first object (that takes part
* in the GC) is sent to native. This could, e.g., be the thread-state dict that is allocated
* when creating the {@link PThreadState native thread state}.
*/
- public Object createGCState() {
+ public long createGCState() {
CompilerAsserts.neverPartOfCompilation();
- assert gcState == null;
+ assert gcState == 0L;
PythonContext.GCState state = getContext().getGcState();
- Object ptr = CStructAccess.AllocateNode.allocUncached(CStructs.GCState);
- CStructAccess.WriteIntNode.writeUncached(ptr, CFields.GCState__enabled, PInt.intValue(state.isEnabled()));
- CStructAccess.WriteIntNode.writeUncached(ptr, CFields.GCState__debug, state.getDebug());
- Object generations = CStructAccess.GetElementPtrNode.getUncached().getElementPtr(ptr, CFields.GCState__generations);
+ long ptr = CStructAccess.allocate(CStructs.GCState);
+ CStructAccess.writeIntField(ptr, CFields.GCState__enabled, PInt.intValue(state.isEnabled()));
+ CStructAccess.writeIntField(ptr, CFields.GCState__debug, state.getDebug());
+ long generations = CStructAccess.getFieldPtr(ptr, CFields.GCState__generations);
for (int i = 0; i < state.getThresholds().length; i++) {
- CStructAccess.WriteIntNode.getUncached().writeStructArrayElement(generations, i, CFields.GCGeneration__threshold, state.getThresholds()[i]);
+ CStructAccess.writeStructArrayIntField(generations, i, CFields.GCGeneration__threshold, state.getThresholds()[i]);
}
gcState = ptr;
return gcState;
@@ -496,8 +509,8 @@ public Object createGCState() {
* {@link #createGCState()} was called the first time which should happen very early during C
* API context initialization.
*/
- public Object getGCState() {
- assert gcState != null;
+ public long getGCState() {
+ assert gcState != 0L;
return gcState;
}
@@ -506,9 +519,10 @@ public Object getGCState() {
*/
private void freeGCState() {
CompilerAsserts.neverPartOfCompilation();
- if (gcState != null) {
- FreeNode.executeUncached(gcState);
- gcState = null;
+ if (gcState != 0L) {
+ LOGGER.fine(String.format("Freeing GC state at 0x%x", gcState));
+ NativeMemory.free(gcState);
+ gcState = 0L;
}
}
@@ -529,8 +543,8 @@ public Object getModuleByIndex(int i) {
* {@link CApiContext} instance (if necessary).
* @return The C API symbol cache.
*/
- private static Object[] getSymbolCache(Node caller) {
- Object[] cache = nativeSymbolCacheSingleContext;
+ private static NativeFunctionPointer[] getSymbolCache(Node caller) {
+ NativeFunctionPointer[] cache = nativeSymbolCacheSingleContext;
if (cache != null) {
return cache;
}
@@ -550,19 +564,13 @@ public static boolean isIdenticalToSymbol(Object obj, NativeCAPISymbol symbol) {
public static boolean isIdenticalToSymbol(long ptr, NativeCAPISymbol symbol) {
CompilerAsserts.neverPartOfCompilation();
- Object nativeSymbol = getNativeSymbol(null, symbol);
- InteropLibrary lib = InteropLibrary.getUncached(nativeSymbol);
- lib.toNative(nativeSymbol);
- try {
- return lib.asPointer(nativeSymbol) == ptr;
- } catch (UnsupportedMessageException e) {
- throw new RuntimeException(e);
- }
+ NativeFunctionPointer nativeSymbol = getNativeSymbol(null, symbol);
+ return nativeSymbol.getAddress() == ptr;
}
- public static Object getNativeSymbol(Node caller, NativeCAPISymbol symbol) {
- Object[] nativeSymbolCache = getSymbolCache(caller);
- Object result = nativeSymbolCache[symbol.ordinal()];
+ public static NativeFunctionPointer getNativeSymbol(Node caller, NativeCAPISymbol symbol) {
+ NativeFunctionPointer[] nativeSymbolCache = getSymbolCache(caller);
+ NativeFunctionPointer result = nativeSymbolCache[symbol.ordinal()];
if (result == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
result = lookupNativeSymbol(nativeSymbolCache, symbol);
@@ -575,26 +583,23 @@ public static Object getNativeSymbol(Node caller, NativeCAPISymbol symbol) {
* Lookup the given C API symbol in the library, store it to the provided cache, and return the
* callable symbol.
*/
- private static Object lookupNativeSymbol(Object[] nativeSymbolCache, NativeCAPISymbol symbol) {
+ private static NativeFunctionPointer lookupNativeSymbol(NativeFunctionPointer[] nativeSymbolCache, NativeCAPISymbol symbol) {
CompilerAsserts.neverPartOfCompilation();
String name = symbol.getName();
- try {
- Object nativeSymbol = InteropLibrary.getUncached().readMember(PythonContext.get(null).getCApiContext().getLibrary(), name);
- nativeSymbol = EnsureExecutableNode.executeUncached(nativeSymbol, symbol);
- VarHandle.storeStoreFence();
- return nativeSymbolCache[symbol.ordinal()] = nativeSymbol;
- } catch (UnsupportedMessageException | UnknownIdentifierException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
+ PythonContext pythonContext = PythonContext.get(null);
+ long nativeSymbolPtr = pythonContext.getCApiContext().getLibrary().lookupSymbol(name);
+ NativeFunctionPointer nativeSymbol = symbol.bind(pythonContext.ensureNativeContext(), nativeSymbolPtr);
+ VarHandle.storeStoreFence();
+ return nativeSymbolCache[symbol.ordinal()] = nativeSymbol;
}
@SuppressWarnings("unused")
- public void trackObject(Object ptr, PFrame.Reference curFrame, TruffleString clazzName) {
+ public void trackObject(long ptr, PFrame.Reference curFrame, TruffleString clazzName) {
// TODO(fa): implement tracking of container objects for cycle detection
}
@SuppressWarnings("unused")
- public void untrackObject(Object ptr, PFrame.Reference curFrame, TruffleString clazzName) {
+ public void untrackObject(long ptr, PFrame.Reference curFrame, TruffleString clazzName) {
// TODO(fa): implement untracking of container objects
}
@@ -608,8 +613,7 @@ private BackgroundGCTask(PythonContext context) {
this.gcRSSMinimum = context.getOption(PythonOptions.BackgroundGCTaskMinimum);
}
- Object nativeSymbol = null;
- InteropLibrary callNative = null;
+ NativeFunctionPointer nativeSymbol = null;
long currentRSS = -1;
long previousRSS = -1;
@@ -642,12 +646,11 @@ private BackgroundGCTask(PythonContext context) {
Long getCurrentRSS() {
if (nativeSymbol == null) {
nativeSymbol = CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_GET_CURRENT_RSS);
- callNative = InteropLibrary.getUncached(nativeSymbol);
}
Long rss = 0L;
try {
- rss = (Long) callNative.execute(nativeSymbol);
- } catch (Exception ignored) {
+ rss = ExternalFunctionInvoker.invokeGET_CURRENT_RSS(nativeSymbol.getAddress());
+ } catch (Throwable ignored) {
}
return rss;
}
@@ -697,7 +700,7 @@ private void perform() {
}
// skip GC if no new native weakrefs have been created.
- int currentWeakrefCount = context.nativeContext.nativeLookup.size();
+ int currentWeakrefCount = context.handleContext.nativeLookup.size();
if (currentWeakrefCount < this.previousWeakrefCount || this.previousWeakrefCount == -1) {
this.previousWeakrefCount = currentWeakrefCount;
return;
@@ -813,6 +816,9 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
if (state == PythonContext.CApiState.INITIALIZED || state == PythonContext.CApiState.INITIALIZING) {
return context.getCApiContext();
}
+ if (state == PythonContext.CApiState.CANNOT_IMPORT) {
+ throw new ImportException(null, name, path, C_API_UNSUPPORTED);
+ }
if (state == PythonContext.CApiState.FAILED) {
throw new ApiInitException(toTruffleStringUncached("The C API initialization has previously failed."));
}
@@ -826,7 +832,7 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
CApiContext cApiContext = loadCApi(node, context, name, path, reason);
assert context.getCApiState() == PythonContext.CApiState.INITIALIZING;
initializeThreadStateCurrentForAttachedThreads(context);
- CApiTransitions.initializeReferenceQueuePolling(context.nativeContext);
+ CApiTransitions.initializeReferenceQueuePolling(context.handleContext);
context.runCApiHooks();
context.setCApiState(PythonContext.CApiState.INITIALIZED); // volatile write
try {
@@ -835,6 +841,9 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
// This can happen when other languages restrict multithreading
LOGGER.warning(() -> "didn't start the background GC task due to: " + e.getMessage());
}
+ } catch (ImportException e) {
+ context.setCApiState(PythonContext.CApiState.CANNOT_IMPORT);
+ throw e;
} catch (Throwable t) {
context.setCApiState(PythonContext.CApiState.FAILED);
throw t;
@@ -875,14 +884,12 @@ private static Thread[] getOtherAliveAttachedThreads(PythonContext context) {
private static CApiContext loadCApi(Node node, PythonContext context, TruffleString name, TruffleString path, String reason) throws IOException, ImportException, ApiInitException {
Env env = context.getEnv();
- InteropLibrary U = InteropLibrary.getUncached();
TruffleFile homePath = env.getInternalTruffleFile(context.getCAPIHome().toJavaStringUncached());
// e.g. "libpython-native.so"
String libName = PythonContext.getSupportLibName("python-native");
final TruffleFile capiFile = homePath.resolve(libName).getCanonicalFile();
try {
- SourceBuilder capiSrcBuilder;
boolean useNative = true;
boolean isolateNative = PythonOptions.IsolateNativeModules.getValue(env.getOptions());
final NativeLibraryLocator loc;
@@ -906,36 +913,39 @@ private static CApiContext loadCApi(Node node, PythonContext context, TruffleStr
}
loc = new NativeLibraryLocator(context, capiFile, isolateNative);
context.ensureNFILanguage(node, "allowNativeAccess", "true");
- String dlopenFlags = isolateNative ? "RTLD_LOCAL" : "RTLD_GLOBAL";
- capiSrcBuilder = Source.newBuilder(J_NFI_LANGUAGE, String.format("load(%s) \"%s\"", dlopenFlags, loc.getCapiLibrary()), "");
+ int dlopenFlags = isolateNative ? PosixConstants.RTLD_LOCAL.value : PosixConstants.RTLD_GLOBAL.value;
LOGGER.config(() -> "loading CAPI from " + loc.getCapiLibrary() + " as native");
- if (!context.getLanguage().getEngineOption(PythonOptions.ExposeInternalSources)) {
- capiSrcBuilder.internal(true);
- }
- CallTarget capiLibraryCallTarget = context.getEnv().parseInternal(capiSrcBuilder.build());
-
- Object capiLibrary = capiLibraryCallTarget.call();
- Object initFunction = U.readMember(capiLibrary, "initialize_graal_capi");
+ NativeContext nativeContext = context.ensureNativeContext();
+ NativeLibrary capiLibrary = nativeContext.loadLibrary(loc.getCapiLibrary(), dlopenFlags);
+ long initFunction = capiLibrary.lookupSymbol("initialize_graal_capi");
CApiContext cApiContext = new CApiContext(context, capiLibrary, loc);
context.setCApiContext(cApiContext);
context.setCApiState(PythonContext.CApiState.INITIALIZING);
- try (BuiltinArrayWrapper builtinArrayWrapper = new BuiltinArrayWrapper()) {
- /*
- * The GC state needs to be created before the first managed object is sent to
- * native. This is because the native object stub could take part in GC and will
- * then already require the GC state.
- */
- Object gcState = cApiContext.createGCState();
- PythonThreadState currentThreadState = context.getThreadState(context.getLanguage());
- Object nativeThreadState = PThreadState.getOrCreateNativeThreadState(currentThreadState);
- Object signature = env.parseInternal(Source.newBuilder(J_NFI_LANGUAGE, "(ENV,POINTER,POINTER,POINTER):POINTER", "exec").build()).call();
- initFunction = SignatureLibrary.getUncached().bind(signature, initFunction);
- Object nativeThreadLocalVarPointer = U.execute(initFunction, builtinArrayWrapper, gcState, nativeThreadState);
- assert U.isPointer(nativeThreadLocalVarPointer);
- assert !U.isNull(nativeThreadLocalVarPointer);
+ /*
+ * The GC state needs to be created before the first managed object is sent to native.
+ * This is because the native object stub could take part in GC and will then already
+ * require the GC state.
+ */
+ long gcState = cApiContext.createGCState();
+ PythonThreadState currentThreadState = context.getThreadState(context.getLanguage());
+ long nativeThreadState = PThreadState.getOrCreateNativeThreadState(currentThreadState);
+
+ long builtinArrayPtr = NativeMemory.mallocPtrArray(PythonCextBuiltinRegistry.builtins.length);
+ try {
+ for (int id = 0; id < PythonCextBuiltinRegistry.builtins.length; id++) {
+ CApiBuiltinExecutable builtin = PythonCextBuiltinRegistry.builtins[id];
+ NativeMemory.writePtrArrayElement(builtinArrayPtr, id, builtin.getNativePointer());
+ }
+ long nativeThreadLocalVarPointer = ExternalFunctionInvoker.invokeCAPIINIT(null, TIMING_INVOKE_CAPI_INIT, nativeContext, BoundaryCallData.getUncached(),
+ context.getThreadState(context.getLanguage()),
+ ExternalFunctionSignature.CAPIINIT.bind(nativeContext, initFunction), builtinArrayPtr, gcState, nativeThreadState);
+ assert nativeThreadLocalVarPointer != NULLPTR;
currentThreadState.setNativeThreadLocalVarPointer(nativeThreadLocalVarPointer);
+ } finally {
+ NativeMemory.free(builtinArrayPtr);
}
+ CApiTransitions.initializeThreadStateDeallocatingOffsets();
assert PythonCApiAssertions.assertBuiltins(capiLibrary);
cApiContext.pyDateTimeCAPICapsule = PyDateTimeCAPIWrapper.initWrapper(context, cApiContext);
@@ -948,9 +958,9 @@ private static CApiContext loadCApi(Node node, PythonContext context, TruffleStr
* it during context exit, but when the VM is terminated by a signal, the context exit
* is skipped. For that case we set up the shutdown hook.
*/
- Object finalizeFunction = U.readMember(capiLibrary, "GraalPyPrivate_GetFinalizeCApiPointer");
- Object finalizeSignature = env.parseInternal(Source.newBuilder(J_NFI_LANGUAGE, "():POINTER", "exec").build()).call();
- Object finalizingPointer = SignatureLibrary.getUncached().call(finalizeSignature, finalizeFunction);
+ long finalizeFunction = capiLibrary.lookupSymbol("GraalPyPrivate_GetFinalizeCApiPointer");
+ long finalizingPointer = ExternalFunctionInvoker.invokeGETFINALIZECAPIPOINTER(null, TIMING_INVOKE_GET_FINALIZE_CAPI_POINTER, nativeContext, BoundaryCallData.getUncached(),
+ context.getThreadState(context.getLanguage()), ExternalFunctionSignature.GETFINALIZECAPIPOINTER.bind(nativeContext, finalizeFunction));
try {
cApiContext.addNativeFinalizer(context, finalizingPointer);
} catch (RuntimeException e) {
@@ -964,7 +974,15 @@ private static CApiContext loadCApi(Node node, PythonContext context, TruffleStr
* Python exceptions that occur during the C API initialization are just passed through
*/
throw e;
- } catch (RuntimeException | UnsupportedMessageException | ArityException | UnknownIdentifierException | UnsupportedTypeException e) {
+ } catch (UnsupportedOperationException e) {
+ assert e.getMessage() != null : "We missed an UnsupportedOperationException that might occur during C API initialization";
+ throw new ImportException(null, name, path, toTruffleStringUncached(e.getMessage()));
+ } catch (NativeLibraryLoadException e) {
+ if (!context.isNativeAccessAllowed()) {
+ throw new ImportException(null, name, path, ErrorMessages.NATIVE_ACCESS_NOT_ALLOWED);
+ }
+ throw new ApiInitException(ErrorMessages.CANNOT_LOAD, libName, e.getMessage());
+ } catch (RuntimeException e) {
// we cannot really check if we truly need native access, so
// when the abi contains "managed" we assume we do not
if (!libName.contains("managed") && !context.isNativeAccessAllowed()) {
@@ -1027,8 +1045,7 @@ public static Object loadCExtModule(Node location, PythonContext context, Module
// we always need to load the CPython C API
CApiContext cApiContext = CApiContext.ensureCapiWasLoaded(location, context, spec.name, spec.path);
- Object library;
- InteropLibrary interopLib;
+ NativeLibrary library;
TruffleFile realPath = context.getPublicTruffleFileRelaxed(spec.path, context.getSoAbi()).getCanonicalFile();
String loadPath = cApiContext.nativeLibraryLocator.resolve(context, realPath);
@@ -1043,20 +1060,18 @@ public static Object loadCExtModule(Node location, PythonContext context, Module
}
dlopenFlags |= PosixConstants.RTLD_LOCAL.value;
}
- String dlopenFlagsString = dlopenFlagsToString(dlopenFlags);
- if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_WIN32) {
- dlopenFlagsString += "| LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR";
- }
- String loadExpr = String.format("load(%s) \"%s\"", dlopenFlagsString, loadPath);
- if (PythonOptions.UsePanama.getValue(context.getEnv().getOptions())) {
- loadExpr = "with panama " + loadExpr;
- }
+
try {
- Source librarySource = Source.newBuilder(J_NFI_LANGUAGE, loadExpr, "load " + spec.name).build();
- library = context.getEnv().parseInternal(librarySource).call();
- interopLib = InteropLibrary.getUncached(library);
+ library = context.ensureNativeContext().loadLibrary(loadPath, dlopenFlags);
} catch (PException e) {
throw e;
+ } catch (NativeLibraryLoadException e) {
+ if (!realPath.exists() && realPath.toString().contains("org.graalvm.python.vfsx")) {
+ getLogger(CApiContext.class).severe(String.format("could not load module %s (real path: %s) from virtual file system.\n\n" +
+ "!!! Please try to run with java system property org.graalvm.python.vfs.extractOnStartup=true !!!\n" +
+ "See also: https://www.graalvm.org/python/docs/#graalpy-troubleshooting", spec.path, realPath));
+ }
+ throw new ImportException(null, spec.name, spec.path, ErrorMessages.CANNOT_LOAD, spec.path, e.getMessage());
} catch (AbstractTruffleException e) {
if (!realPath.exists() && realPath.toString().contains("org.graalvm.python.vfsx")) {
// file does not exist and it is from VirtualFileSystem
@@ -1069,12 +1084,7 @@ public static Object loadCExtModule(Node location, PythonContext context, Module
throw new ImportException(CExtContext.wrapJavaException(e, location), spec.name, spec.path, ErrorMessages.CANNOT_LOAD_M, spec.path, e);
}
-
- try {
- return cApiContext.initCApiModule(location, library, spec.getInitFunctionName(), spec, interopLib);
- } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
- throw new ImportException(CExtContext.wrapJavaException(e, location), spec.name, spec.path, ErrorMessages.CANNOT_INITIALIZE_WITH, spec.path, spec.getEncodedName(), "");
- }
+ return cApiContext.initCApiModule(location, library, spec.getInitFunctionName(), spec);
}
/**
@@ -1088,21 +1098,15 @@ public static Object loadCExtModule(Node location, PythonContext context, Module
* triggered a dlclose that dropped the refcount of the python-native library to 0. We leak 1
* byte of memory and this shutdown hook for each context that ever initialized the C API.
*/
- private void addNativeFinalizer(PythonContext context, Object finalizingPointerObj) {
+ private void addNativeFinalizer(PythonContext context, long finalizingPointer) {
final Unsafe unsafe = context.getUnsafe();
- InteropLibrary lib = InteropLibrary.getUncached(finalizingPointerObj);
- if (!lib.isNull(finalizingPointerObj) && lib.isPointer(finalizingPointerObj)) {
- try {
- long finalizingPointer = lib.asPointer(finalizingPointerObj);
- // We are writing off heap memory and registering a VM shutdown hook, there is no
- // point in creating this thread via Truffle sandbox at this point
- nativeFinalizerRunnable = () -> unsafe.putByte(finalizingPointer, (byte) 1);
- context.registerAtexitHook((c) -> nativeFinalizerRunnable.run());
- nativeFinalizerShutdownHook = new Thread(nativeFinalizerRunnable);
- Runtime.getRuntime().addShutdownHook(nativeFinalizerShutdownHook);
- } catch (UnsupportedMessageException e) {
- throw new RuntimeException(e);
- }
+ if (finalizingPointer != 0L) {
+ // We are writing off heap memory and registering a VM shutdown hook, there is no
+ // point in creating this thread via Truffle sandbox at this point
+ nativeFinalizerRunnable = () -> unsafe.putByte(finalizingPointer, (byte) 1);
+ context.registerAtexitHook((c) -> nativeFinalizerRunnable.run());
+ nativeFinalizerShutdownHook = new Thread(nativeFinalizerRunnable);
+ Runtime.getRuntime().addShutdownHook(nativeFinalizerShutdownHook);
}
}
@@ -1126,21 +1130,15 @@ public void exitCApiContext() {
* interpreter.
*/
pollReferenceQueue();
- PythonThreadState threadState = getContext().getThreadState(getContext().getLanguage());
- Object nativeThreadState = PThreadState.getNativeThreadState(threadState);
- if (nativeThreadState != null) {
- PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_PY_GC_COLLECT_NO_FAIL, nativeThreadState);
- pollReferenceQueue();
- }
CApiTransitions.deallocateNativeWeakRefs(getContext());
}
}
@SuppressWarnings("try")
- public void finalizeCApi() {
+ public void finalizeCApi(boolean cancelling) {
CompilerAsserts.neverPartOfCompilation();
PythonContext context = getContext();
- HandleContext handleContext = context.nativeContext;
+ HandleContext handleContext = context.handleContext;
if (backgroundGCTaskThread != null && backgroundGCTaskThread.isAlive()) {
context.killSystemThread(backgroundGCTaskThread);
try {
@@ -1163,9 +1161,15 @@ public void finalizeCApi() {
try {
// TODO(fa): remove GIL acquisition (GR-51314)
try (GilNode.UncachedAcquire ignored = GilNode.uncachedAcquire()) {
- // First we want to free all replacements for which we have to call tp_dealloc,
- // while all our stubs are still available for the tp_dealloc code to run.
- CApiTransitions.deallocNativeReplacements(context, handleContext);
+ /*
+ * First we want to free all replacements for which we have to call tp_dealloc,
+ * while all our stubs are still available for the tp_dealloc code to run. Since
+ * tp_dealloc may run arbitrary user code, we must not do that if the context was
+ * canceled.
+ */
+ if (!cancelling) {
+ CApiTransitions.deallocNativeReplacements(context, handleContext);
+ }
// The singletons can be freed now
freeSingletonNativeWrappers(handleContext);
// Now we can clear all native memory that was simply allocated from Java. This
@@ -1179,7 +1183,7 @@ public void finalizeCApi() {
PyDateTimeCAPIWrapper.destroyWrapper(pyDateTimeCAPICapsule);
}
// free all allocated PyMethodDef structures
- for (Object pyMethodDefPointer : methodDefinitions.values()) {
+ for (Long pyMethodDefPointer : methodDefinitions.values()) {
PyMethodDefHelper.free(pyMethodDefPointer);
}
} finally {
@@ -1213,33 +1217,18 @@ public void finalizeCApi() {
}
@TruffleBoundary
- public Object initCApiModule(Node node, Object sharedLibrary, TruffleString initFuncName, ModuleSpec spec, InteropLibrary llvmInteropLib)
- throws UnsupportedMessageException, ArityException, UnsupportedTypeException, ImportException {
+ public Object initCApiModule(Node node, NativeLibrary sharedLibrary, TruffleString initFuncName, ModuleSpec spec) throws ImportException {
PythonContext context = getContext();
CApiContext cApiContext = context.getCApiContext();
- Object pyinitFunc;
- try {
- pyinitFunc = llvmInteropLib.readMember(sharedLibrary, initFuncName.toJavaStringUncached());
- } catch (UnknownIdentifierException | UnsupportedMessageException e1) {
+ long pyinitFunc = sharedLibrary.lookupOptionalSymbol(initFuncName.toJavaStringUncached());
+ if (pyinitFunc == 0L) {
throw new ImportException(null, spec.name, spec.path, ErrorMessages.NO_FUNCTION_FOUND, "", initFuncName, spec.path);
}
- Object nativeResult;
- try {
- nativeResult = InteropLibrary.getUncached().execute(pyinitFunc);
- } catch (UnsupportedMessageException e) {
- Object signature = context.getEnv().parseInternal(MODINIT_SRC).call();
- nativeResult = SignatureLibrary.getUncached().call(signature, pyinitFunc);
- } catch (ArityException e) {
- // In case of multi-phase init, the init function may take more than one argument.
- // However, CPython gracefully ignores that. So, we pass just NULL pointers.
- Object[] arguments = new Object[e.getExpectedMinArity()];
- Arrays.fill(arguments, PNone.NO_VALUE);
- nativeResult = InteropLibrary.getUncached().execute(pyinitFunc, arguments);
- }
-
- ExternalFunctionNodesFactory.DefaultCheckFunctionResultNodeGen.getUncached().execute(context, initFuncName, nativeResult);
+ NativeContext nativeContext = context.ensureNativeContext();
+ long nativeResult = ExternalFunctionInvoker.invokeMODINIT(null, TIMING_INVOKE_MODULE_INIT, nativeContext, BoundaryCallData.getUncached(), context.getThreadState(context.getLanguage()),
+ ExternalFunctionSignature.MODINIT.bind(nativeContext, pyinitFunc));
- Object result = NativeToPythonNode.executeUncached(nativeResult);
+ Object result = PyObjectCheckFunctionResultNodeGen.getUncached().execute(context, initFuncName, NativeToPythonNode.executeUncached(nativeResult));
if (!(result instanceof PythonModule)) {
// Multi-phase extension module initialization
@@ -1255,7 +1244,7 @@ public Object initCApiModule(Node node, Object sharedLibrary, TruffleString init
throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.INIT_FUNC_RETURNED_UNINT_OBJ, initFuncName);
}
- return CExtNodes.createModule(node, cApiContext, spec, result, sharedLibrary);
+ return CExtNodes.createModule(node, cApiContext, spec, nativeResult, sharedLibrary);
} else {
// see: 'import.c: _PyImport_FixupExtensionObject'
PythonModule module = (PythonModule) result;
@@ -1267,8 +1256,8 @@ public Object initCApiModule(Node node, Object sharedLibrary, TruffleString init
sysModules.setItem(spec.name, result);
// _PyState_AddModule
- Object moduleDef = module.getNativeModuleDef();
- int mIndex = PythonUtils.toIntError(CStructAccess.ReadI64Node.getUncached().read(moduleDef, CFields.PyModuleDef_Base__m_index));
+ long moduleDef = module.getNativeModuleDef();
+ int mIndex = PythonUtils.toIntError(readLongField(moduleDef, CFields.PyModuleDef_Base__m_index));
while (modulesByIndex.size() <= mIndex) {
modulesByIndex.add(null);
}
@@ -1285,114 +1274,12 @@ public PythonModule findExtension(TruffleString filename, TruffleString name) {
return extensions.get(Pair.create(filename, name));
}
- /**
- * An array wrapper around {@link PythonCextBuiltinRegistry#builtins} which also implements
- * {@link InteropLibrary#toNative(Object)}. This is intended to be passed to the C API
- * initialization function. In order to avoid memory leaks if the wrapper receives
- * {@code toNative}, it should be used in a try-with-resources.
- */
- @ExportLibrary(InteropLibrary.class)
- @SuppressWarnings("static-method")
- static final class BuiltinArrayWrapper implements TruffleObject, AutoCloseable {
- private long pointer;
-
- @ExportMessage
- boolean hasArrayElements() {
- return true;
- }
-
- @ExportMessage
- long getArraySize() {
- return PythonCextBuiltinRegistry.builtins.length;
- }
-
- @ExportMessage
- boolean isArrayElementReadable(long index) {
- return 0 <= index && index < PythonCextBuiltinRegistry.builtins.length;
- }
-
- @ExportMessage
- @TruffleBoundary
- Object readArrayElement(long index) throws InvalidArrayIndexException {
- if (!isArrayElementReadable(index)) {
- throw InvalidArrayIndexException.create(index);
- }
- // cast is guaranteed by 'isArrayElementReadable'
- return getCAPIBuiltinExecutable((int) index);
- }
-
- private static CApiBuiltinExecutable getCAPIBuiltinExecutable(int id) {
- CompilerAsserts.neverPartOfCompilation();
- try {
- CApiBuiltinExecutable builtin = PythonCextBuiltinRegistry.builtins[id];
- LOGGER.finer("CApiContext.BuiltinArrayWrapper.get " + id + " / " + builtin.name());
- return builtin;
- } catch (Throwable e) {
- // this is a fatal error, so print it to stderr:
- e.printStackTrace(new PrintStream(PythonContext.get(null).getEnv().err()));
- throw new RuntimeException(e);
- }
- }
-
- @ExportMessage
- boolean isPointer() {
- return pointer != 0;
- }
-
- @ExportMessage
- long asPointer() throws UnsupportedMessageException {
- if (pointer != 0) {
- return pointer;
- }
- throw UnsupportedMessageException.create();
- }
-
- @ExportMessage
- @TruffleBoundary
- void toNative() {
- if (pointer == 0) {
- assert PythonContext.get(null).isNativeAccessAllowed();
- Object ptr = CStructAccess.AllocateNode.callocUncached(PythonCextBuiltinRegistry.builtins.length, CStructAccess.POINTER_SIZE);
- pointer = CExtCommonNodes.CoerceNativePointerToLongNode.executeUncached(ptr);
- if (pointer != 0) {
- InteropLibrary lib = null;
- for (int i = 0; i < PythonCextBuiltinRegistry.builtins.length; i++) {
- CApiBuiltinExecutable capiBuiltinExecutable = getCAPIBuiltinExecutable(i);
- if (lib == null || !lib.accepts(capiBuiltinExecutable)) {
- lib = InteropLibrary.getUncached(capiBuiltinExecutable);
- }
- assert lib.accepts(capiBuiltinExecutable);
- lib.toNative(capiBuiltinExecutable);
- try {
- CStructAccess.WritePointerNode.writeArrayElementUncached(pointer, i, lib.asPointer(capiBuiltinExecutable));
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
- }
- }
- }
-
- @Override
- public void close() {
- if (pointer != 0) {
- FreeNode.executeUncached(pointer);
- }
- }
- }
-
public long getClosurePointer(Object executable) {
CompilerAsserts.neverPartOfCompilation();
ClosureInfo info = callableClosureByExecutable.get(executable);
return info == null ? -1 : info.pointer;
}
- public Object getClosureForExecutable(Object executable) {
- CompilerAsserts.neverPartOfCompilation();
- ClosureInfo info = callableClosureByExecutable.get(executable);
- return info == null ? null : info.closure;
- }
-
public Object getClosureDelegate(long pointer) {
CompilerAsserts.neverPartOfCompilation();
ClosureInfo info = callableClosures.get(pointer);
@@ -1405,36 +1292,25 @@ public Object getClosureExecutable(long pointer) {
return info == null ? null : info.executable;
}
- public void setClosurePointer(Object closure, Object delegate, Object executable, long pointer) {
+ public void setClosurePointer(Object delegate, Object executable, long pointer) {
CompilerAsserts.neverPartOfCompilation();
- var info = new ClosureInfo(closure, delegate, executable, pointer);
+ var info = new ClosureInfo(delegate, executable, pointer);
callableClosureByExecutable.put(executable, info);
callableClosures.put(pointer, info);
- LOGGER.finer(() -> PythonUtils.formatJString("new NFI closure: (%s, %s) -> %d 0x%x", executable.getClass().getSimpleName(), delegate, pointer, pointer));
- }
-
- private static Source buildNFISource(Object srcObj) {
- return Source.newBuilder(J_NFI_LANGUAGE, (String) srcObj, "exec").build();
+ LOGGER.finer(() -> PythonUtils.formatJString("new native closure: (%s, %s) -> %d 0x%x", executable.getClass().getSimpleName(), delegate, pointer, pointer));
}
- public long registerClosure(String nfiSignature, Object executable, Object delegate, SignatureLibrary signatureLibrary) {
+ public long registerClosure(String name, NativeSignature signature, MethodHandle methodHandle, Object key, Object delegate) {
CompilerAsserts.neverPartOfCompilation();
PythonContext context = getContext();
- boolean panama = context.getOption(PythonOptions.UsePanama);
- String srcString = (panama ? "with panama " : "") + nfiSignature;
- Source nfiSource = context.getLanguage().getOrCreateSource(CApiContext::buildNFISource, srcString);
- Object signature = context.getEnv().parseInternal(nfiSource).call();
- Object closure = signatureLibrary.createClosure(signature, executable);
- long pointer = PythonUtils.coerceToLong(closure, InteropLibrary.getUncached());
- setClosurePointer(closure, delegate, executable, pointer);
+ long pointer = signature.createClosure(context.ensureNativeContext(), name, methodHandle);
+ setClosurePointer(delegate, key, pointer);
return pointer;
}
@TruffleBoundary
- public Object getOrAllocateNativePyMethodDef(PyMethodDefHelper pyMethodDef) {
- Object pyMethodDefPointer = methodDefinitions.computeIfAbsent(pyMethodDef, PyMethodDefHelper::allocate);
- assert CApiContext.isPointerObject(pyMethodDefPointer);
- return pyMethodDefPointer;
+ public long getOrAllocateNativePyMethodDef(PyMethodDefHelper pyMethodDef) {
+ return methodDefinitions.computeIfAbsent(pyMethodDef, PyMethodDefHelper::allocate);
}
/**
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
index e64fb48c9f..2d124e3dca 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -375,6 +375,8 @@ public final class CApiFunction {
@CApiBuiltin(name = "PyNumber_AsSsize_t", ret = Py_ssize_t, args = {PyObject, PyObject}, call = CImpl)
@CApiBuiltin(name = "PyNumber_Check", ret = Int, args = {PyObject}, call = CImpl)
@CApiBuiltin(name = "PyNumber_Divmod", ret = PyObject, args = {PyObject, PyObject}, call = CImpl)
+ @CApiBuiltin(name = "PyNumber_Float", ret = PyObject, args = {PyObject}, call = CImpl)
+ @CApiBuiltin(name = "PyNumber_Long", ret = PyObject, args = {PyObject}, call = CImpl)
@CApiBuiltin(name = "PyNumber_FloorDivide", ret = PyObject, args = {PyObject, PyObject}, call = CImpl)
@CApiBuiltin(name = "PyNumber_InPlaceAdd", ret = PyObject, args = {PyObject, PyObject}, call = CImpl)
@CApiBuiltin(name = "PyNumber_InPlaceAnd", ret = PyObject, args = {PyObject, PyObject}, call = CImpl)
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiGCSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiGCSupport.java
index 7b54da03c8..3e2838f1ef 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiGCSupport.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiGCSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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,23 +41,23 @@
package com.oracle.graal.python.builtins.objects.cext.capi;
import static com.oracle.graal.python.builtins.objects.cext.capi.CApiContext.GC_LOGGER;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeLongField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.free;
import java.util.logging.Level;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiGCSupportFactory.GCListRemoveNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiGCSupportFactory.PyObjectGCDelNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CoerceNativePointerToLongNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.FreeNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
@@ -66,8 +66,6 @@
import com.oracle.truffle.api.nodes.Node;
public abstract class CApiGCSupport {
- public static final CApiTiming VISIT_TIMING = CApiTiming.create(true, PExternalFunctionWrapper.VISITPROC);
-
public static final long NEXT_MASK_UNREACHABLE = 1;
/* Bit 0 is set when tp_finalize is called */
@@ -104,49 +102,74 @@ static long computePrevValue(long curPrevValue, long newValue) {
@GenerateCached(false)
public abstract static class PyObjectGCTrackNode extends Node {
- public abstract void execute(Node inliningTarget, long gc);
+ /**
+ * Track the Python object denoted by the given {@code PyObject*} pointer. This will apply
+ * {@code AS_GC(op)} first.
+ */
+ public final void executeOp(Node inliningTarget, long op) {
+ if (PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC)) {
+ // AS_GC(op)
+ execute(inliningTarget, op - CStructs.PyGC_Head.size());
+ }
+ }
+
+ /**
+ * Track the Python object denoted by the given {@code _PyGC_Head*} pointer.
+ */
+ public final void executeGc(Node inliningTarget, long gc) {
+ if (PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC)) {
+ execute(inliningTarget, gc);
+ }
+ }
+
+ abstract void execute(Node inliningTarget, long gc);
@Specialization
- static void doGeneric(Node inliningTarget, long gc,
- @Cached CoerceNativePointerToLongNode coerceToLongNode,
- @Cached(inline = false) CStructAccess.ReadPointerNode readPointerNode,
- @Cached(inline = false) CStructAccess.ReadI64Node readI64Node,
- @Cached(inline = false) CStructAccess.WriteLongNode writeLongNode) {
+ static void doGeneric(Node inliningTarget, long gc) {
+ assert PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC);
long gcUntagged = HandlePointerConverter.pointerToStub(gc);
// #define _PyObject_GC_IS_TRACKED(o) (_PyGCHead_UNTAG(_Py_AS_GC(o))->_gc_next != 0)
- long gcNext = readI64Node.read(gcUntagged, CFields.PyGC_Head___gc_next);
+ long gcNext = readLongField(gcUntagged, CFields.PyGC_Head___gc_next);
// if (!_PyObject_GC_IS_TRACKED(op))
if (gcNext == 0) {
if (GC_LOGGER.isLoggable(Level.FINER)) {
GC_LOGGER.finer(PythonUtils.formatJString("tracking GC object 0x%x (op=0x%x)", gc, gc + CStructs.PyGC_Head.size()));
}
// PyGC_Head *generation0 = tstate->gc->generation0;
- Object gcState = PythonContext.get(inliningTarget).getCApiContext().getGCState();
- assert gcState != null;
- long gen0 = coerceToLongNode.execute(inliningTarget, readPointerNode.read(gcState, CFields.GCState__generation0));
+ long gcState = PythonContext.get(inliningTarget).getCApiContext().getGCState();
+ assert gcState != 0L;
+ long gen0 = CStructAccess.readPtrField(gcState, CFields.GCState__generation0);
assert gen0 != 0;
assert !HandlePointerConverter.pointsToPyHandleSpace(gen0);
// PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev);
- long last = readI64Node.read(gen0, CFields.PyGC_Head___gc_prev);
+ long last = readLongField(gen0, CFields.PyGC_Head___gc_prev);
// _PyGCHead_SET_NEXT(last, gc);
- writeLongNode.write(HandlePointerConverter.pointerToStub(last), CFields.PyGC_Head___gc_next, gc);
+ CStructAccess.writeLongField(HandlePointerConverter.pointerToStub(last), CFields.PyGC_Head___gc_next, gc);
// _PyGCHead_SET_PREV(gc, last);
- long curGcPrev = readI64Node.read(gcUntagged, CFields.PyGC_Head___gc_prev);
- writeLongNode.write(gcUntagged, CFields.PyGC_Head___gc_prev, computePrevValue(curGcPrev, last));
+ long curGcPrev = readLongField(gcUntagged, CFields.PyGC_Head___gc_prev);
+ CStructAccess.writeLongField(gcUntagged, CFields.PyGC_Head___gc_prev, computePrevValue(curGcPrev, last));
// _PyGCHead_SET_NEXT(gc, generation0);
- writeLongNode.write(gcUntagged, CFields.PyGC_Head___gc_next, gen0);
+ CStructAccess.writeLongField(gcUntagged, CFields.PyGC_Head___gc_next, gen0);
// generation0->_gc_prev = (uintptr_t)gc;
- writeLongNode.write(gen0, CFields.PyGC_Head___gc_prev, gc);
+ CStructAccess.writeLongField(gen0, CFields.PyGC_Head___gc_prev, gc);
} else if (GC_LOGGER.isLoggable(Level.FINER)) {
GC_LOGGER.finer(PythonUtils.formatJString("GC object 0x%x (op=0x%x) already tracked", gc, gc + CStructs.PyGC_Head.size()));
}
}
+
+ @TruffleBoundary(allowInlining = true)
+ public static boolean isGcTracked(long taggedPointer) {
+ // #define _PyObject_GC_IS_TRACKED(o) (_PyGCHead_UNTAG(_Py_AS_GC(o))->_gc_next != 0)
+ long gcUntagged = HandlePointerConverter.pointerToStub(taggedPointer - CStructs.PyGC_Head.size());
+ long gcNext = readLongField(gcUntagged, CFields.PyGC_Head___gc_next);
+ return gcNext != 0;
+ }
}
/**
@@ -180,11 +203,8 @@ public final void executeOp(Node inliningTarget, long op) {
public abstract long execute(Node inliningTarget, long opUntagged);
@Specialization
- static long doGeneric(long opUntagged,
- @Cached(inline = false) CStructAccess.ReadI64Node readI64Node,
- @Cached(inline = false) CStructAccess.WriteLongNode writeLongNode) {
+ static long doGeneric(long opUntagged) {
assert PythonLanguage.get(null).getEngineOption(PythonOptions.PythonGC);
-
// issue a log message before doing the first memory access
if (GC_LOGGER.isLoggable(Level.FINER)) {
GC_LOGGER.finer(PythonUtils.formatJString("attempting to remove 0x%x from GC generation", opUntagged));
@@ -204,7 +224,7 @@ static long doGeneric(long opUntagged,
*/
// #define _PyObject_GC_IS_TRACKED(o) (_PyGCHead_UNTAG(_Py_AS_GC(o))->_gc_next != 0)
- long gcNext = readI64Node.read(gcUntagged, CFields.PyGC_Head___gc_next);
+ long gcNext = readLongField(gcUntagged, CFields.PyGC_Head___gc_next);
// if (_PyObject_GC_IS_TRACKED(op))
if (gcNext != 0) {
// gc_list_remove
@@ -213,20 +233,25 @@ static long doGeneric(long opUntagged,
}
// PyGC_Head *prev = GC_PREV(gc)
- long prev = maskPrevValue(readI64Node.read(gcUntagged, CFields.PyGC_Head___gc_prev));
+ long prev = maskPrevValue(readLongField(gcUntagged, CFields.PyGC_Head___gc_prev));
// PyGC_Head *next = GC_NEXT(gc)
- long next = readI64Node.read(gcUntagged, CFields.PyGC_Head___gc_next);
+ long next = readLongField(gcUntagged, CFields.PyGC_Head___gc_next);
+ /*
+ * We need to remove NEXT_MASK_UNREACHABLE because the object we are up to remove
+ * may be in GC list 'weak_candidates' which sets the bit.
+ */
+ long nextUntagged = HandlePointerConverter.pointerToStub(next & ~CApiGCSupport.NEXT_MASK_UNREACHABLE);
// _PyGCHead_SET_NEXT(prev, next)
- writeLongNode.write(HandlePointerConverter.pointerToStub(prev), CFields.PyGC_Head___gc_next, next);
+ writeLongField(HandlePointerConverter.pointerToStub(prev), CFields.PyGC_Head___gc_next, next);
// _PyGCHead_SET_PREV(next, prev)
- long curNextPrev = readI64Node.read(HandlePointerConverter.pointerToStub(next), CFields.PyGC_Head___gc_prev);
- writeLongNode.write(HandlePointerConverter.pointerToStub(next), CFields.PyGC_Head___gc_prev, computePrevValue(curNextPrev, prev));
+ long curNextPrev = readLongField(nextUntagged, CFields.PyGC_Head___gc_prev);
+ writeLongField(nextUntagged, CFields.PyGC_Head___gc_prev, computePrevValue(curNextPrev, prev));
// UNTAG(gc)->_gc_next = 0
- writeLongNode.write(gcUntagged, CFields.PyGC_Head___gc_next, 0);
+ writeLongField(gcUntagged, CFields.PyGC_Head___gc_next, 0);
} else {
/*
* This is a valid case because objects can manually be untracked or removed from GC
@@ -258,10 +283,7 @@ public static void executeUncached(long op) {
@Specialization
static void doGeneric(Node inliningTarget, long op,
- @Cached GCListRemoveNode gcListRemoveNode,
- @Cached(inline = false) CStructAccess.GetElementPtrNode getElementPtrNode,
- @Cached(inline = false) CStructAccess.ReadI32Node readI32Node,
- @Cached(inline = false) CStructAccess.WriteIntNode writeIntNode) {
+ @Cached GCListRemoveNode gcListRemoveNode) {
if (GC_LOGGER.isLoggable(Level.FINE)) {
GC_LOGGER.fine(PythonUtils.formatJString("releasing native object stub 0x%x", op));
}
@@ -276,19 +298,19 @@ static void doGeneric(Node inliningTarget, long op,
long gcUntagged = gcListRemoveNode.execute(inliningTarget, opUntagged);
// GCState *gcstate = get_gc_state();
- Object gcState = PythonContext.get(inliningTarget).getCApiContext().getGCState();
- assert gcState != null;
+ long gcState = PythonContext.get(inliningTarget).getCApiContext().getGCState();
+ assert gcState != 0L;
// compute start address of embedded array; essentially '&gcstate->generations[0]'
- Object generations = getElementPtrNode.getElementPtr(gcState, CFields.GCState__generations);
+ long generations = CStructAccess.getFieldPtr(gcState, CFields.GCState__generations);
// if (gcstate->generations[0].count > 0) {
- int count = readI32Node.read(generations, CFields.GCGeneration__count);
+ int count = CStructAccess.readStructArrayIntField(generations, 0, CFields.GCGeneration__count);
if (count > 0) {
// gcstate->generations[0].count--;
- writeIntNode.write(generations, CFields.GCGeneration__count, count - 1);
+ CStructAccess.writeStructArrayIntField(generations, 0, CFields.GCGeneration__count, count - 1);
}
// PyObject_Free(((char *)op)-presize)
- FreeNode.executeUncached(gcUntagged);
+ free(gcUntagged);
}
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiMemberAccessNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiMemberAccessNodes.java
index 1489b0765f..796d661a83 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiMemberAccessNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiMemberAccessNodes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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
@@ -45,6 +45,7 @@
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.BadMemberDescrNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.NativePtrToPythonWrapperNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.ReadMemberNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.ReadOnlyMemberNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.WriteByteNodeGen;
@@ -59,11 +60,9 @@
import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.WriteShortNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.WriteUIntNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodesFactory.WriteULongNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativePtrToPythonNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtAsPythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.AsNativeCharNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.AsNativeDoubleNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.AsNativePrimitiveNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.NativePrimitiveAsPythonBooleanNodeGen;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.NativePrimitiveAsPythonCharNodeGen;
@@ -71,12 +70,15 @@
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.NativeUnsignedPrimitiveAsPythonObjectNodeGen;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.NativeUnsignedShortNodeGen;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.StringAsPythonStringNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtToJavaNode;
+import com.oracle.graal.python.builtins.objects.cext.structs.CConstants;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorDeleteMarker;
import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode;
+import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode;
@@ -130,48 +132,7 @@ private static boolean isReadOnlyType(int type) {
return type == T_STRING || type == T_STRING_INPLACE;
}
- private static CStructAccess.ReadBaseNode getReadNode(int type) {
- switch (type) {
- case T_SHORT:
- case T_USHORT:
- return CStructAccessFactory.ReadI16NodeGen.create();
- case T_INT:
- case T_UINT:
- return CStructAccessFactory.ReadI32NodeGen.create();
- case T_LONG:
- case T_ULONG:
- return CStructAccessFactory.ReadI64NodeGen.create();
- case T_FLOAT:
- return CStructAccessFactory.ReadFloatNodeGen.create();
- case T_DOUBLE:
- return CStructAccessFactory.ReadDoubleNodeGen.create();
- case T_STRING:
- return CStructAccessFactory.ReadPointerNodeGen.create();
- case T_OBJECT:
- return CStructAccessFactory.ReadObjectNodeGen.create();
- case T_OBJECT_EX:
- return CStructAccessFactory.ReadObjectNodeGen.create();
- case T_CHAR:
- case T_BYTE:
- case T_UBYTE:
- case T_BOOL:
- return CStructAccessFactory.ReadByteNodeGen.create();
- case T_STRING_INPLACE:
- return CStructAccessFactory.GetElementPtrNodeGen.create();
- case T_LONGLONG:
- case T_ULONGLONG:
- assert CStructs.long__long.size() == Long.BYTES;
- return CStructAccessFactory.ReadI64NodeGen.create();
- case T_PYSSIZET:
- assert CStructs.Py_ssize_t.size() == Long.BYTES;
- return CStructAccessFactory.ReadI64NodeGen.create();
- case T_NONE:
- return null;
- }
- throw CompilerDirectives.shouldNotReachHere("invalid member type");
- }
-
- private static CExtAsPythonObjectNode getReadConverterNode(int type) {
+ private static CExtToJavaNode getReadConverterNode(int type) {
switch (type) {
case T_SHORT:
case T_INT:
@@ -201,19 +162,25 @@ private static CExtAsPythonObjectNode getReadConverterNode(int type) {
return NativeUnsignedPrimitiveAsPythonObjectNodeGen.create();
case T_OBJECT:
case T_OBJECT_EX:
- return null;
+ return NativePtrToPythonWrapperNodeGen.create();
}
throw CompilerDirectives.shouldNotReachHere("invalid member type");
}
+ @GenerateInline(false)
+ abstract static class NativePtrToPythonWrapperNode extends CExtToJavaNode {
+ @Specialization
+ Object doGeneric(long pointer,
+ @Cached NativePtrToPythonNode nativePtrToPythonNode) {
+ return nativePtrToPythonNode.execute(pointer, false);
+ }
+ }
+
@Builtin(name = "read_member", minNumOfPositionalArgs = 1, parameterNames = "$self")
public abstract static class ReadMemberNode extends PythonUnaryBuiltinNode {
private static final Builtin BUILTIN = ReadMemberNode.class.getAnnotation(Builtin.class);
- @Child private PythonToNativeNode toSulongNode;
- @Child private CExtAsPythonObjectNode asPythonObjectNode;
-
- @Child private CStructAccess.ReadBaseNode read;
+ @Child private CExtToJavaNode asPythonObjectNode;
/** The specified member type. */
private final int type;
@@ -221,48 +188,69 @@ public abstract static class ReadMemberNode extends PythonUnaryBuiltinNode {
/** The offset where to read from (will be passed to the native getter). */
private final int offset;
- protected ReadMemberNode(int type, int offset, CExtAsPythonObjectNode asPythonObjectNode) {
+ protected ReadMemberNode(int type, int offset, CExtToJavaNode asPythonObjectNode) {
this.type = type;
- this.read = getReadNode(type);
this.offset = offset;
this.asPythonObjectNode = asPythonObjectNode;
}
+ protected static boolean isCharSigned() {
+ return CConstants.CHAR_MIN.longValue() < 0;
+ }
+
@Specialization
Object doGeneric(@SuppressWarnings("unused") VirtualFrame frame, Object self,
@Bind Node inliningTarget,
+ @Cached PythonToNativeNode toNativeNode,
@Cached PRaiseNode raiseNode) {
- if (read == null) {
- return PNone.NONE;
- } else {
- Object nativeResult = read.readGeneric(ensureToSulongNode().execute(self), offset);
- assert !(nativeResult instanceof Byte || nativeResult instanceof Short || nativeResult instanceof Float || nativeResult instanceof Character || nativeResult instanceof PException ||
- nativeResult instanceof String) : nativeResult + " " + nativeResult.getClass();
- if (type == T_OBJECT_EX && nativeResult == PNone.NO_VALUE) {
- throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError);
+ long selfPtr = toNativeNode.executeLong(self);
+ long memberPtr = NativeMemory.getFieldPtr(selfPtr, offset);
+ Object nativeResult = switch (type) {
+ case T_CHAR, T_BYTE, T_UBYTE, T_BOOL -> {
+ byte n = NativeMemory.readByte(memberPtr);
+ if (isCharSigned()) { // TODO(native-access) profile
+ yield (int) n;
+ }
+ yield Byte.toUnsignedInt(n);
}
- if (type == T_OBJECT && nativeResult == PNone.NO_VALUE) {
- nativeResult = PNone.NONE;
+ case T_SHORT, T_USHORT -> (int) NativeMemory.readShort(memberPtr);
+ case T_INT, T_UINT -> NativeMemory.readInt(memberPtr);
+ case T_LONG, T_ULONG -> NativeMemory.readLong(memberPtr);
+ case T_FLOAT -> (double) NativeMemory.readFloat(memberPtr);
+ case T_DOUBLE -> NativeMemory.readDouble(memberPtr);
+ case T_OBJECT, T_OBJECT_EX -> NativeMemory.readPtr(memberPtr);
+ case T_STRING -> NativeMemory.readPtr(memberPtr);
+ case T_STRING_INPLACE -> memberPtr;
+ case T_LONGLONG, T_ULONGLONG -> {
+ assert CStructs.long__long.size() == Long.BYTES;
+ yield NativeMemory.readLong(memberPtr);
}
- if (asPythonObjectNode != null) {
- return asPythonObjectNode.execute(nativeResult);
- } else {
- return nativeResult;
+ case T_PYSSIZET -> {
+ assert CStructs.Py_ssize_t.size() == Long.BYTES;
+ yield NativeMemory.readLong(memberPtr);
}
+ case T_NONE -> PNone.NONE;
+ default -> throw CompilerDirectives.shouldNotReachHere("invalid member type");
+ };
+
+ assert !(nativeResult instanceof Byte || nativeResult instanceof Short || nativeResult instanceof Float || nativeResult instanceof Character || nativeResult instanceof PException ||
+ nativeResult instanceof String) : nativeResult + " " + nativeResult.getClass();
+ if (asPythonObjectNode != null) {
+ // TODO(native-access) some of these could be inlined into the switch above
+ nativeResult = asPythonObjectNode.execute(nativeResult);
}
- }
-
- private PythonToNativeNode ensureToSulongNode() {
- if (toSulongNode == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- toSulongNode = insert(PythonToNativeNodeGen.create());
+ if (type == T_OBJECT_EX && nativeResult == PNone.NO_VALUE) {
+ throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError);
}
- return toSulongNode;
+ if (type == T_OBJECT && nativeResult == PNone.NO_VALUE) {
+ nativeResult = PNone.NONE;
+ }
+ return nativeResult;
}
@TruffleBoundary
public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, Object owner, TruffleString propertyName, int type, int offset) {
- CExtAsPythonObjectNode asPythonObjectNode = getReadConverterNode(type);
+ CExtToJavaNode asPythonObjectNode = getReadConverterNode(type);
RootCallTarget callTarget = language.createCachedPropAccessCallTarget(
l -> new BuiltinFunctionRootNode(l, BUILTIN, new PrototypeNodeFactory<>(ReadMemberNodeGen.create(type, offset, asPythonObjectNode)), true),
ReadMemberNode.class, BUILTIN.name(), type, offset);
@@ -323,17 +311,16 @@ public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, Tr
abstract static class WriteTypeNode extends Node {
- abstract void execute(Object pointer, Object newValue);
+ abstract void execute(long pointer, Object newValue);
}
@GenerateInline(false)
abstract static class WriteByteNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
- @Cached AsNativePrimitiveNode asLong,
- @Cached CStructAccess.WriteByteNode write) {
- write.write(pointer, (byte) asLong.toInt64(newValue, true));
+ static void write(long pointer, Object newValue,
+ @Cached AsNativePrimitiveNode asLong) {
+ NativeMemory.writeByte(pointer, (byte) asLong.toInt64(newValue, true));
}
}
@@ -341,10 +328,9 @@ static void write(Object pointer, Object newValue,
abstract static class WriteShortNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
- @Cached AsNativePrimitiveNode asLong,
- @Cached CStructAccess.WriteI16Node write) {
- write.write(pointer, (short) asLong.toInt64(newValue, true));
+ static void write(long pointer, Object newValue,
+ @Cached AsNativePrimitiveNode asLong) {
+ NativeMemory.writeShort(pointer, (short) asLong.toInt64(newValue, true));
}
}
@@ -352,10 +338,9 @@ static void write(Object pointer, Object newValue,
abstract static class WriteIntNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
- @Cached AsNativePrimitiveNode asLong,
- @Cached CStructAccess.WriteIntNode write) {
- write.write(pointer, (int) asLong.toInt64(newValue, true));
+ static void write(long pointer, Object newValue,
+ @Cached AsNativePrimitiveNode asLong) {
+ NativeMemory.writeInt(pointer, (int) asLong.toInt64(newValue, true));
}
}
@@ -363,13 +348,12 @@ static void write(Object pointer, Object newValue,
abstract static class WriteLongNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
+ static void write(long pointer, Object newValue,
@Bind Node inliningTarget,
@Cached AsNativePrimitiveNode asLong,
- @Cached CStructAccess.WriteLongNode write,
@Cached IsBuiltinObjectProfile exceptionProfile) {
try {
- write.write(pointer, asLong.toInt64(newValue, true));
+ NativeMemory.writeLong(pointer, asLong.toInt64(newValue, true));
} catch (PException e) {
/*
* Special case: if conversion raises an OverflowError, CPython still assigns the
@@ -377,7 +361,7 @@ static void write(Object pointer, Object newValue,
* just do the same.
*/
e.expectOverflowError(inliningTarget, exceptionProfile);
- write.write(pointer, -1);
+ NativeMemory.writeLong(pointer, -1);
throw e;
}
}
@@ -387,23 +371,22 @@ static void write(Object pointer, Object newValue,
abstract static class WriteUIntNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
+ static void write(long pointer, Object newValue,
@Bind Node inliningTarget,
@Cached AsNativePrimitiveNode asLong,
- @Cached CStructAccess.WriteIntNode write,
@Cached IsBuiltinObjectProfile exceptionProfile) {
/*
* This emulates the arguably buggy behavior from CPython where it accepts MIN_LONG to
* MAX_ULONG values.
*/
try {
- write.write(pointer, (int) asLong.toUInt64(newValue, true));
+ NativeMemory.writeInt(pointer, (int) asLong.toUInt64(newValue, true));
} catch (PException e) {
/*
* Special case: accept signed long as well.
*/
e.expectOverflowError(inliningTarget, exceptionProfile);
- write.write(pointer, (int) asLong.toInt64(newValue, true));
+ NativeMemory.writeInt(pointer, (int) asLong.toInt64(newValue, true));
// swallowing the exception
}
}
@@ -413,13 +396,12 @@ static void write(Object pointer, Object newValue,
abstract static class WriteULongNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
+ static void write(long pointer, Object newValue,
@Bind Node inliningTarget,
@Cached AsNativePrimitiveNode asLong,
- @Cached CStructAccess.WriteLongNode write,
@Cached IsBuiltinObjectProfile exceptionProfile) {
try {
- write.write(pointer, asLong.toUInt64(newValue, true));
+ NativeMemory.writeLong(pointer, asLong.toUInt64(newValue, true));
} catch (PException e) {
/*
* Special case: if conversion raises an OverflowError, CPython still assigns the
@@ -427,7 +409,7 @@ static void write(Object pointer, Object newValue,
* just do the same.
*/
e.expectOverflowError(inliningTarget, exceptionProfile);
- write.write(pointer, -1);
+ NativeMemory.writeLong(pointer, -1);
throw e;
}
}
@@ -437,13 +419,12 @@ static void write(Object pointer, Object newValue,
abstract static class WriteDoubleNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
+ static void write(long pointer, Object newValue,
@Bind Node inliningTarget,
- @Cached AsNativeDoubleNode asDouble,
- @Cached CStructAccess.WriteDoubleNode write,
+ @Cached PyFloatAsDoubleNode asDouble,
@Cached IsBuiltinObjectProfile exceptionProfile) {
try {
- write.write(pointer, asDouble.executeDouble(newValue));
+ NativeMemory.writeDouble(pointer, asDouble.execute(null, inliningTarget, newValue));
} catch (PException e) {
/*
* Special case: if conversion raises an OverflowError, CPython still assigns the
@@ -451,7 +432,7 @@ static void write(Object pointer, Object newValue,
* just do the same.
*/
e.expectTypeError(inliningTarget, exceptionProfile);
- write.write(pointer, -1);
+ NativeMemory.writeDouble(pointer, -1);
throw e;
}
}
@@ -461,10 +442,10 @@ static void write(Object pointer, Object newValue,
abstract static class WriteFloatNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
- @Cached AsNativeDoubleNode asDouble,
- @Cached CStructAccess.WriteFloatNode write) {
- write.write(pointer, (float) asDouble.executeDouble(newValue));
+ static void write(long pointer, Object newValue,
+ @Bind Node inliningTarget,
+ @Cached PyFloatAsDoubleNode asDouble) {
+ NativeMemory.writeFloat(pointer, (float) asDouble.execute(null, inliningTarget, newValue));
}
}
@@ -472,7 +453,7 @@ static void write(Object pointer, Object newValue,
abstract static class WriteObjectNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
+ static void write(long pointer, Object newValue,
@Cached CStructAccess.WriteObjectNewRefNode write) {
write.write(pointer, newValue);
}
@@ -482,12 +463,12 @@ static void write(Object pointer, Object newValue,
abstract static class WriteObjectExNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
+ static void write(long pointer, Object newValue,
@Bind Node inliningTarget,
@Cached CStructAccess.ReadObjectNode read,
@Cached CStructAccess.WriteObjectNewRefNode write,
@Cached PRaiseNode raise) {
- Object current = read.readGeneric(pointer, 0);
+ Object current = read.read(pointer, 0);
if (newValue == DescriptorDeleteMarker.INSTANCE && current == PNone.NO_VALUE) {
throw raise.raise(inliningTarget, PythonBuiltinClassType.AttributeError);
}
@@ -499,10 +480,9 @@ static void write(Object pointer, Object newValue,
abstract static class WriteCharNode extends WriteTypeNode {
@Specialization
- static void write(Object pointer, Object newValue,
- @Cached AsNativeCharNode asChar,
- @Cached CStructAccess.WriteByteNode write) {
- write.write(pointer, asChar.executeByte(newValue));
+ static void write(long pointer, Object newValue,
+ @Cached AsNativeCharNode asChar) {
+ NativeMemory.writeByte(pointer, asChar.executeByte(newValue));
}
}
@@ -552,11 +532,10 @@ private static WriteTypeNode getWriteNode(int type) {
public abstract static class WriteMemberNode extends PythonBinaryBuiltinNode {
private static final Builtin BUILTIN = WriteMemberNode.class.getAnnotation(Builtin.class);
- @Child private PythonToNativeNode toSulongNode;
+ @Child private PythonToNativeNode toNativeNode;
@Child private GetClassNode getClassNode;
@Child private IsSameTypeNode isSameTypeNode;
@Child private WriteTypeNode write;
- @Child private CStructAccess.GetElementPtrNode getElement;
/** The specified member type. */
private final int type;
@@ -568,16 +547,14 @@ protected WriteMemberNode(int type, int offset) {
this.type = type;
this.offset = offset;
this.write = getWriteNode(type);
- this.toSulongNode = PythonToNativeNodeGen.create();
- this.getElement = CStructAccessFactory.GetElementPtrNodeGen.create();
+ this.toNativeNode = PythonToNativeNode.create();
}
@Specialization
Object doGeneric(Object self, Object value,
@Bind Node inliningTarget,
@Cached PRaiseNode raiseNode) {
- Object selfPtr = toSulongNode.execute(self);
- selfPtr = getElement.readGeneric(selfPtr, offset);
+ long selfPtr = toNativeNode.executeLong(self) + offset;
/*
* Deleting values is only allowed for members with object type (see structmember.c:
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java
index b2fcbd59ad..70bce259a9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java
@@ -45,13 +45,10 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_GRAALPY_OBJECT_GC_DEL;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_NO_OP_CLEAR;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_NO_OP_TRAVERSE;
-import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PTR_COMPARE;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_DEALLOC;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_OBJECT_FREE;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_TYPE_GENERIC_ALLOC;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_SUBTYPE_TRAVERSE;
-import static com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper.IMMORTAL_REFCNT;
-import static com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper.MANAGED_REFCNT;
import static com.oracle.graal.python.builtins.objects.cext.structs.CConstants.PYLONG_BITS_IN_DIGIT;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyFloatObject__ob_fval;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMethodDef__ml_doc;
@@ -64,22 +61,28 @@
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_methods;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_size;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyModuleDef__m_slots;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_refcnt;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_type;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_as_buffer;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readDoubleField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayPtrField;
+import static com.oracle.graal.python.builtins.objects.object.PythonObject.MANAGED_REFCNT;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.calloc;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.mallocByteArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElement;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElements;
import static com.oracle.graal.python.nodes.HiddenAttr.METHOD_DEF_PTR;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___COMPLEX__;
-import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE;
import static com.oracle.graal.python.runtime.exception.PythonErrorType.SystemError;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
-import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
-import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.lang.ref.Reference;
+import java.util.logging.Level;
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
@@ -88,36 +91,24 @@
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext.ModuleSpec;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.AsCharPointerNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.EnsurePythonObjectNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.FromCharPointerNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.ResolvePointerNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.UnicodeFromFormatNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckPrimitiveFunctionResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.DefaultCheckFunctionResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonClassInternalNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.ResolveHandleNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.UpdateStrongRefNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.NativeToPythonNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CArrayWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CByteArrayWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CStringWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.EnsureExecutableNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.EnsureTruffleStringNode;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionFromNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
-import com.oracle.graal.python.builtins.objects.cext.common.GetNextVaArgNode;
-import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
@@ -145,10 +136,8 @@
import com.oracle.graal.python.builtins.objects.type.TypeNodes.ProfileClassNode;
import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
-import com.oracle.graal.python.lib.PyObjectLookupAttr;
import com.oracle.graal.python.lib.PyObjectSizeNode;
-import com.oracle.graal.python.lib.RichCmpOp;
-import com.oracle.graal.python.nodes.BuiltinNames;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.HiddenAttr;
import com.oracle.graal.python.nodes.PGuards;
@@ -156,23 +145,17 @@
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.SpecialAttributeNames;
import com.oracle.graal.python.nodes.SpecialMethodNames;
-import com.oracle.graal.python.nodes.StringLiterals;
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode;
-import com.oracle.graal.python.nodes.call.CallNode;
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.LookupAndCallUnaryDynamicNode;
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode;
-import com.oracle.graal.python.nodes.truffle.PythonIntegerTypes;
-import com.oracle.graal.python.nodes.util.CannotCastException;
-import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
-import com.oracle.graal.python.nodes.util.CastToJavaStringNodeGen;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
-import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
import com.oracle.graal.python.runtime.object.PFactory;
@@ -182,9 +165,9 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateCached;
@@ -193,22 +176,14 @@
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.dsl.TypeSystemReference;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.ArityException;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.interop.UnsupportedTypeException;
-import com.oracle.truffle.api.library.CachedLibrary;
+import com.oracle.truffle.api.nodes.EncapsulatingNodeReference;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
-import com.oracle.truffle.api.profiles.InlinedConditionProfile;
-import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.profiles.InlinedExactClassProfile;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.strings.TruffleString.Encoding;
-import com.oracle.truffle.nfi.api.SignatureLibrary;
public abstract class CExtNodes {
@@ -220,95 +195,75 @@ public abstract class CExtNodes {
* will call that subtype C function with two arguments, the C type object and an object
* argument to fill in from.
*/
- @ImportStatic({PGuards.class})
- @GenerateInline(false) // footprint reduction 44 -> 25
- public abstract static class SubtypeNew extends Node {
-
- /**
- * tget the typename_subtype_new function
- */
- protected NativeCAPISymbol getFunction() {
- throw shouldNotReachHere();
- }
+ @GenerateInline
+ @GenerateCached(false)
+ public abstract static class FloatSubtypeNew extends Node {
- protected abstract Object execute(Object object, Object arg);
+ public abstract Object execute(Node inliningTarget, Object object, double arg);
@Specialization
- Object callNativeConstructor(Object object, Object arg,
+ static Object doGeneric(Object object, double arg,
@Bind Node inliningTarget,
- @Cached PythonToNativeNode toSulongNode,
- @Cached NativeToPythonTransferNode toJavaNode,
- @CachedLibrary(limit = "1") InteropLibrary interopLibrary) {
+ @Cached PythonToNativeNode toNativeNode,
+ @Cached NativeToPythonTransferNode toJavaNode) {
assert TypeNodes.NeedsNativeAllocationNode.executeUncached(object);
+ NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_FLOAT_SUBTYPE_NEW);
try {
- Object callable = CApiContext.getNativeSymbol(inliningTarget, getFunction());
- Object result = interopLibrary.execute(callable, toSulongNode.execute(object), arg);
+ long result = ExternalFunctionInvoker.invokeFLOAT_SUBTYPE_NEW(callable.getAddress(), toNativeNode.executeLong(object), arg);
return toJavaNode.execute(result);
- } catch (UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
- throw shouldNotReachHere("C subtype_new function failed", e);
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
}
}
}
- @GenerateInline(false) // footprint reduction 44 -> 25
- public abstract static class FloatSubtypeNew extends SubtypeNew {
-
- @Override
- protected final NativeCAPISymbol getFunction() {
- return NativeCAPISymbol.FUN_FLOAT_SUBTYPE_NEW;
- }
-
- public final Object call(Object object, double arg) {
- return execute(object, arg);
- }
-
- public static FloatSubtypeNew create() {
- return CExtNodesFactory.FloatSubtypeNewNodeGen.create();
- }
- }
-
- public abstract static class TupleSubtypeNew extends SubtypeNew {
-
- @Child private PythonToNativeNode toNativeNode;
+ @GenerateInline
+ @GenerateCached(false)
+ public abstract static class TupleSubtypeNew extends Node {
- @Override
- protected final NativeCAPISymbol getFunction() {
- return NativeCAPISymbol.FUN_TUPLE_SUBTYPE_NEW;
- }
+ public abstract Object execute(Node inliningTarget, Object object, Object arg);
- public final Object call(Object object, Object arg) {
- if (toNativeNode == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- toNativeNode = insert(PythonToNativeNodeGen.create());
+ @Specialization
+ static Object doGeneric(Node inliningTarget, Object object, Object arg,
+ @Cached PythonToNativeInternalNode toNativeNode,
+ @Cached NativeToPythonInternalNode toJavaNode) {
+ assert TypeNodes.NeedsNativeAllocationNode.executeUncached(object);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(arg);
+ NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_TUPLE_SUBTYPE_NEW);
+ try {
+ long result = ExternalFunctionInvoker.invokeTUPLE_SUBTYPE_NEW(callable.getAddress(),
+ toNativeNode.execute(inliningTarget, object, false),
+ toNativeNode.execute(inliningTarget, arg, false));
+ return toJavaNode.execute(inliningTarget, result, true);
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
}
- return execute(object, toNativeNode.execute(arg));
- }
-
- @NeverDefault
- public static TupleSubtypeNew create() {
- return CExtNodesFactory.TupleSubtypeNewNodeGen.create();
}
}
- public abstract static class StringSubtypeNew extends SubtypeNew {
+ @GenerateInline
+ @GenerateCached(false)
+ public abstract static class StringSubtypeNew extends Node {
- @Child private PythonToNativeNode toNativeNode;
+ public abstract Object execute(Node inliningTarget, Object object, Object arg);
- @Override
- protected final NativeCAPISymbol getFunction() {
- return NativeCAPISymbol.FUN_UNICODE_SUBTYPE_NEW;
- }
-
- public final Object call(Object object, Object arg) {
- if (toNativeNode == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- toNativeNode = insert(PythonToNativeNodeGen.create());
+ @Specialization
+ static Object doGeneric(Node inliningTarget, Object object, Object arg,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached PythonToNativeInternalNode toNativeNode,
+ @Cached NativeToPythonInternalNode toJavaNode) {
+ assert TypeNodes.NeedsNativeAllocationNode.executeUncached(object);
+ NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_UNICODE_SUBTYPE_NEW);
+ try {
+ Object promotedArg = ensurePythonObjectNode.execute(PythonContext.get(inliningTarget), arg, false);
+ long result = ExternalFunctionInvoker.invokeUNICODE_SUBTYPE_NEW(callable.getAddress(),
+ toNativeNode.execute(inliningTarget, object, false),
+ toNativeNode.execute(inliningTarget, promotedArg, false));
+ Reference.reachabilityFence(promotedArg);
+ return toJavaNode.execute(inliningTarget, result, true);
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
}
- return execute(object, toNativeNode.execute(arg));
- }
-
- public static StringSubtypeNew create() {
- return CExtNodesFactory.StringSubtypeNewNodeGen.create();
}
}
@@ -320,14 +275,23 @@ public abstract static class DictSubtypeNew extends Node {
@Specialization
static PDict allocateNativePart(Object cls, PDict managedSide,
- @Cached PythonToNativeNode toNative,
- @Cached PCallCapiFunction call) {
- assert managedSide.getNativeWrapper() == null;
- var nativeWrapper = new PythonObjectNativeWrapper(managedSide);
- managedSide.setNativeWrapper(nativeWrapper);
- long nativeObject = (long) call.call(NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW_RAW, toNative.execute(cls), 0L, 0L);
+ @Bind Node inliningTarget,
+ @Cached PythonToNativeNode toNative) {
+ assert !managedSide.isNative();
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(cls);
+ long clsPointer = toNative.executeLong(cls);
+ long nativeObject;
+ try {
+ nativeObject = ExternalFunctionInvoker.invokePY_TYPE_GENERIC_NEW_RAW(CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW_RAW).getAddress(),
+ clsPointer, 0L, 0L);
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ } finally {
+ Reference.reachabilityFence(cls);
+ }
CApiTransitions.writeNativeRefCount(nativeObject, MANAGED_REFCNT);
- CApiTransitions.createReference(nativeWrapper, nativeObject, false);
+ CApiTransitions.createReference(managedSide, nativeObject);
+ assert managedSide.isNative();
return managedSide;
}
}
@@ -342,10 +306,9 @@ public abstract static class FromNativeSubclassNode extends Node {
static Double doDouble(PythonAbstractNativeObject object,
@Bind Node inliningTarget,
@Cached GetPythonObjectClassNode getClass,
- @Cached IsSubtypeNode isSubtype,
- @Cached CStructAccess.ReadDoubleNode read) {
+ @Cached IsSubtypeNode isSubtype) {
if (isFloatSubtype(inliningTarget, object, getClass, isSubtype)) {
- return read.readFromObj(object, PyFloatObject__ob_fval);
+ return readDoubleField(object.getPtr(), PyFloatObject__ob_fval);
}
return null;
}
@@ -364,165 +327,61 @@ public static FromNativeSubclassNode create() {
}
}
- // -----------------------------------------------------------------------------------------------------------------
-
- /**
- * Materializes a primitive value of a primitive native wrapper to ensure pointer equality.
- */
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- @ImportStatic(CApiGuards.class)
- public abstract static class MaterializeDelegateNode extends Node {
-
- public abstract Object execute(Node inliningTarget, PythonNativeWrapper object);
-
- @Specialization(guards = {"!isMaterialized(object)", "object.isBool()"})
- static PInt doBoolNativeWrapper(Node inliningTarget, PrimitiveNativeWrapper object) {
- // Special case for True and False: use singletons
- Python3Core core = PythonContext.get(inliningTarget);
- PInt materializedInt = object.getBool() ? core.getTrue() : core.getFalse();
- object.setMaterializedObject(materializedInt);
-
- // If the singleton already has a native wrapper, we may need to update the pointer
- // of wrapper 'object' since the native could code see the same pointer.
- if (materializedInt.getNativeWrapper() != null) {
- object.setNativePointer(materializedInt.getNativeWrapper().getNativePointer());
- } else {
- materializedInt.setNativeWrapper(object);
- }
- return materializedInt;
- }
-
- @Specialization(guards = {"!isMaterialized(object)", "object.isInt()"})
- static PInt doIntNativeWrapper(PrimitiveNativeWrapper object,
- @Bind PythonLanguage language) {
- PInt materializedInt = PFactory.createInt(language, object.getInt());
- object.setMaterializedObject(materializedInt);
- materializedInt.setNativeWrapper(object);
- return materializedInt;
- }
-
- @Specialization(guards = {"!isMaterialized(object)", "object.isLong()"})
- static PInt doLongNativeWrapper(PrimitiveNativeWrapper object,
- @Bind PythonLanguage language) {
- PInt materializedInt = PFactory.createInt(language, object.getLong());
- object.setMaterializedObject(materializedInt);
- materializedInt.setNativeWrapper(object);
- return materializedInt;
- }
-
- @Specialization(guards = {"!isMaterialized(object)", "object.isDouble()", "!isNaN(object)"})
- static PFloat doDoubleNativeWrapper(PrimitiveNativeWrapper object,
- @Bind PythonLanguage language) {
- PFloat materializedInt = PFactory.createFloat(language, object.getDouble());
- materializedInt.setNativeWrapper(object);
- object.setMaterializedObject(materializedInt);
- return materializedInt;
- }
-
- @Specialization(guards = {"!isMaterialized(object)", "object.isDouble()", "isNaN(object)"})
- static PFloat doDoubleNativeWrapperNaN(Node inliningTarget, PrimitiveNativeWrapper object) {
- // Special case for double NaN: use singleton
- PFloat materializedFloat = PythonContext.get(inliningTarget).getNaN();
- object.setMaterializedObject(materializedFloat);
-
- // If the NaN singleton already has a native wrapper, we may need to update the
- // pointer
- // of wrapper 'object' since the native code should see the same pointer.
- if (materializedFloat.getNativeWrapper() != null) {
- object.setNativePointer(materializedFloat.getNativeWrapper().getNativePointer());
- } else {
- materializedFloat.setNativeWrapper(object);
- }
- return materializedFloat;
- }
-
- @Specialization(guards = "isMaterialized(object)")
- static Object doMaterialized(PrimitiveNativeWrapper object) {
- return object.getDelegate();
- }
-
- @Specialization(guards = "!isPrimitiveNativeWrapper(object)")
- static Object doNativeWrapperGeneric(PythonNativeWrapper object) {
- return object.getDelegate();
- }
-
- protected static boolean isPrimitiveNativeWrapper(PythonNativeWrapper object) {
- return object instanceof PrimitiveNativeWrapper;
- }
-
- protected static boolean isNaN(PrimitiveNativeWrapper object) {
- assert object.isDouble();
- return Double.isNaN(object.getDouble());
- }
-
- static boolean isMaterialized(PrimitiveNativeWrapper wrapper) {
- return wrapper.getDelegate() != null;
- }
- }
-
// -----------------------------------------------------------------------------------------------------------------
@GenerateUncached
@GenerateInline(false) // footprint reduction 60 -> 41
public abstract static class AsCharPointerNode extends Node {
- public abstract Object execute(Object obj, boolean allocatePyMem);
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(AsCharPointerNode.class);
- public abstract Object execute(TruffleString obj, boolean allocatePyMem);
-
- public final Object execute(Object obj) {
- return execute(obj, false);
- }
+ public abstract long execute(Object obj);
@Specialization
- static Object doPString(PString str, boolean allocatePyMem,
+ static long doPString(PString str,
@Bind Node inliningTarget,
@Cached CastToTruffleStringNode castToStringNode,
@Shared @Cached TruffleString.SwitchEncodingNode switchEncoding,
- @Shared @Cached CStructAccess.AllocateNode alloc,
@Shared @Cached CStructAccess.WriteTruffleStringNode writeTruffleString) {
TruffleString value = castToStringNode.execute(inliningTarget, str);
- TruffleString utf8Str = switchEncoding.execute(value, Encoding.UTF_8);
- Object mem = alloc.alloc(utf8Str.byteLength(Encoding.UTF_8) + 1, allocatePyMem);
- writeTruffleString.write(mem, utf8Str, Encoding.UTF_8);
- return mem;
+ return doString(value, switchEncoding, writeTruffleString);
}
@Specialization
- static Object doString(TruffleString str, boolean allocatePyMem,
+ static long doString(TruffleString str,
@Shared @Cached TruffleString.SwitchEncodingNode switchEncoding,
- @Shared @Cached CStructAccess.AllocateNode alloc,
@Shared @Cached CStructAccess.WriteTruffleStringNode writeTruffleString) {
TruffleString utf8Str = switchEncoding.execute(str, Encoding.UTF_8);
- Object mem = alloc.alloc(utf8Str.byteLength(Encoding.UTF_8) + 1, allocatePyMem);
+ int size = utf8Str.byteLength(Encoding.UTF_8) + 1;
+ long mem = calloc(size);
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Allocated (const char *)0x%x of size %d for %s", mem, size, utf8Str));
+ }
writeTruffleString.write(mem, utf8Str, Encoding.UTF_8);
return mem;
}
@Specialization
- static Object doBytes(PBytes bytes, boolean allocatePyMem,
+ static long doBytes(PBytes bytes,
@Bind Node inliningTarget,
- @Shared @Cached SequenceStorageNodes.ToByteArrayNode toBytesNode,
- @Shared @Cached CStructAccess.AllocateNode alloc,
- @Shared @Cached CStructAccess.WriteByteNode write) {
- return doByteArray(toBytesNode.execute(inliningTarget, bytes.getSequenceStorage()), allocatePyMem, alloc, write);
+ @Shared @Cached SequenceStorageNodes.ToByteArrayNode toBytesNode) {
+ return doByteArray(toBytesNode.execute(inliningTarget, bytes.getSequenceStorage()));
}
@Specialization
- static Object doBytes(PByteArray bytes, boolean allocatePyMem,
+ static long doBytes(PByteArray bytes,
@Bind Node inliningTarget,
- @Shared @Cached SequenceStorageNodes.ToByteArrayNode toBytesNode,
- @Shared @Cached CStructAccess.AllocateNode alloc,
- @Shared @Cached CStructAccess.WriteByteNode write) {
- return doByteArray(toBytesNode.execute(inliningTarget, bytes.getSequenceStorage()), allocatePyMem, alloc, write);
+ @Shared @Cached SequenceStorageNodes.ToByteArrayNode toBytesNode) {
+ return doByteArray(toBytesNode.execute(inliningTarget, bytes.getSequenceStorage()));
}
@Specialization
- static Object doByteArray(byte[] arr, boolean allocatePyMem,
- @Shared @Cached CStructAccess.AllocateNode alloc,
- @Shared @Cached CStructAccess.WriteByteNode write) {
- Object mem = alloc.alloc(arr.length + 1, allocatePyMem);
- write.writeByteArray(mem, arr);
+ static long doByteArray(byte[] arr) {
+ long size = arr.length + 1L;
+ long mem = mallocByteArray(size);
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Allocated (const char *)0x%x of size %d for (byte[])%s", mem, size, arr));
+ }
+ writeByteArrayElements(mem, 0, arr, 0, arr.length);
+ writeByteArrayElement(mem, arr.length, (byte) 0);
return mem;
}
@@ -533,58 +392,31 @@ public static AsCharPointerNode getUncached() {
// -----------------------------------------------------------------------------------------------------------------
@GenerateUncached
- @GenerateInline(false) // footprint reduction 36 -> 17
+ @GenerateInline
+ @GenerateCached(false)
public abstract static class FromCharPointerNode extends Node {
- public final TruffleString execute(Object charPtr) {
- return execute(charPtr, true);
+ public final TruffleString execute(Node inliningTarget, long charPtr) {
+ return execute(inliningTarget, charPtr, true);
}
- public static TruffleString executeUncached(Object charPtr) {
- return FromCharPointerNodeGen.getUncached().execute(charPtr);
+ @TruffleBoundary
+ public static TruffleString executeUncached(long charPtr) {
+ return FromCharPointerNodeGen.getUncached().execute(null, charPtr, true);
}
- public static TruffleString executeUncached(Object charPtr, boolean copy) {
- return FromCharPointerNodeGen.getUncached().execute(charPtr, copy);
+ @TruffleBoundary
+ public static TruffleString executeUncached(long charPtr, boolean copy) {
+ return FromCharPointerNodeGen.getUncached().execute(null, charPtr, copy);
}
- public abstract TruffleString execute(Object charPtr, boolean copy);
-
- @Specialization
- static TruffleString doCStringWrapper(CStringWrapper cStringWrapper, @SuppressWarnings("unused") boolean copy) {
- return cStringWrapper.getString();
- }
+ public abstract TruffleString execute(Node inliningTarget, long charPtr, boolean copy);
@Specialization
- static TruffleString doCByteArrayWrapper(CByteArrayWrapper cByteArrayWrapper, boolean copy,
- @Shared @Cached TruffleString.FromByteArrayNode fromBytes,
- @Shared("switchEncoding") @Cached TruffleString.SwitchEncodingNode switchEncodingNode) {
- CompilerAsserts.partialEvaluationConstant(copy);
- byte[] byteArray = cByteArrayWrapper.getByteArray();
- return switchEncodingNode.execute(fromBytes.execute(byteArray, 0, byteArray.length, Encoding.UTF_8, copy), TS_ENCODING);
- }
-
- @Specialization(guards = "!isCArrayWrapper(charPtr)", limit = "3")
- static TruffleString doPointer(Object charPtr, boolean copy,
- @Cached CStructAccess.ReadByteNode read,
- @CachedLibrary("charPtr") InteropLibrary lib,
- @Cached TruffleString.FromNativePointerNode fromNative,
- @Shared @Cached TruffleString.FromByteArrayNode fromBytes,
- @Shared("switchEncoding") @Cached TruffleString.SwitchEncodingNode switchEncodingNode) {
-
- int length = 0;
- while (read.readArrayElement(charPtr, length) != 0) {
- length++;
- }
-
- if (lib.isPointer(charPtr)) {
- return switchEncodingNode.execute(fromNative.execute(charPtr, 0, length, Encoding.UTF_8, copy), TS_ENCODING);
- }
- byte[] result = read.readByteArray(charPtr, length);
- return switchEncodingNode.execute(fromBytes.execute(result, Encoding.UTF_8, false), TS_ENCODING);
- }
-
- static boolean isCArrayWrapper(Object object) {
- return object instanceof CArrayWrapper || object instanceof PySequenceArrayWrapper;
+ static TruffleString doPointer(long charPtr, boolean copy,
+ @Cached(inline = false) TruffleString.FromZeroTerminatedNativePointerNode fromNativePointerNode,
+ @Cached(inline = false) TruffleString.SwitchEncodingNode switchEncodingNode) {
+ TruffleString nativeBacked = fromNativePointerNode.execute8Bit(charPtr, 0, Encoding.UTF_8, copy);
+ return switchEncodingNode.execute(nativeBacked, TS_ENCODING);
}
}
@@ -597,57 +429,11 @@ public abstract static class GetNativeClassNode extends PNodeWithContext {
@Specialization
static Object getNativeClass(Node inliningTarget, PythonAbstractNativeObject object,
- @Cached(inline = false) CStructAccess.ReadObjectNode callGetObTypeNode,
+ @Cached NativeToPythonClassInternalNode nativeToPythonClassInternalNode,
@Cached ProfileClassNode classProfile) {
- // do not convert wrap 'object.object' since that is really the native pointer object
- return classProfile.profile(inliningTarget, callGetObTypeNode.readFromObj(object, PyObject__ob_type));
- }
- }
-
- @GenerateUncached
- @GenerateInline
- @GenerateCached(false)
- public abstract static class PointerCompareNode extends Node {
-
- public abstract boolean execute(Node inliningTarget, RichCmpOp op, Object a, Object b);
-
- @Specialization
- static boolean doGeneric(Node inliningTarget, RichCmpOp op, Object a, Object b,
- @Cached NormalizePtrNode normalizeA,
- @Cached NormalizePtrNode normalizeB,
- @CachedLibrary(limit = "1") InteropLibrary interopLibrary) {
- CompilerAsserts.partialEvaluationConstant(op);
- Object ptrA = normalizeA.execute(inliningTarget, a);
- Object ptrB = normalizeB.execute(inliningTarget, b);
- try {
- Object sym = CApiContext.getNativeSymbol(inliningTarget, FUN_PTR_COMPARE);
- return (int) interopLibrary.execute(sym, ptrA, ptrB, op.asNative()) != 0;
- } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
-
- @TypeSystemReference(PythonIntegerTypes.class)
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- abstract static class NormalizePtrNode extends Node {
- abstract Object execute(Node inliningTarget, Object ptr);
-
- @Specialization
- static Object doLong(long l) {
- return l;
- }
-
- @Specialization
- static Object doLong(PythonNativeObject o) {
- return o.getPtr();
- }
-
- @Specialization
- static Object doLong(PythonNativeVoidPtr o) {
- return o.getNativePointer();
- }
+ long obType = readPtrField(object.pointer, PyObject__ob_type);
+ Object type = nativeToPythonClassInternalNode.execute(inliningTarget, obType);
+ return classProfile.profile(inliningTarget, type);
}
}
@@ -803,78 +589,8 @@ static long doPInt(PInt value) {
static long doPFloat(PFloat value) {
return (long) value.getValue();
}
-
- @Specialization
- static Object doPythonNativeVoidPtr(PythonNativeVoidPtr object) {
- return object.getPointerObject();
- }
-
- @Specialization(guards = "!object.isDouble()")
- static long doLongNativeWrapper(PrimitiveNativeWrapper object) {
- return object.getLong();
- }
-
- @Specialization(guards = "object.isDouble()")
- static long doDoubleNativeWrapper(PrimitiveNativeWrapper object) {
- return (long) object.getDouble();
- }
-
- @Specialization
- static Object run(Node inliningTarget, PythonNativeWrapper value,
- @Cached(inline = false) CastToNativeLongNode recursive) {
- // TODO(fa) this specialization should eventually go away
- return recursive.execute(inliningTarget, value.getDelegate());
- }
}
- // -----------------------------------------------------------------------------------------------------------------
- @GenerateUncached
- @GenerateInline(false) // footprint reduction 40 -> 21
- public abstract static class PCallCapiFunction extends Node {
-
- public static Object callUncached(NativeCAPISymbol symbol, Object... args) {
- return PCallCapiFunction.getUncached().execute(symbol, args);
- }
-
- public final Object call(NativeCAPISymbol symbol, Object... args) {
- return execute(symbol, args);
- }
-
- protected abstract Object execute(NativeCAPISymbol symbol, Object[] args);
-
- @Specialization
- static Object doWithoutContext(NativeCAPISymbol symbol, Object[] args,
- @Bind Node inliningTarget,
- @CachedLibrary(limit = "1") InteropLibrary interopLibrary,
- @Cached EnsureTruffleStringNode ensureTruffleStringNode) {
- try {
- PythonContext pythonContext = PythonContext.get(inliningTarget);
- PythonContext.CApiState capiState = pythonContext.getCApiState();
- if (capiState != PythonContext.CApiState.INITIALIZING && capiState != PythonContext.CApiState.INITIALIZED) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- CApiContext.ensureCapiWasLoaded("call internal native GraalPy function");
- }
- // TODO review EnsureTruffleStringNode with GR-37896
- Object callable = CApiContext.getNativeSymbol(inliningTarget, symbol);
- return ensureTruffleStringNode.execute(inliningTarget, interopLibrary.execute(callable, args));
- } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
- // consider these exceptions to be fatal internal errors
- throw shouldNotReachHere(e);
- }
- }
-
- @NeverDefault
- public static PCallCapiFunction create() {
- return CExtNodesFactory.PCallCapiFunctionNodeGen.create();
- }
-
- public static PCallCapiFunction getUncached() {
- return CExtNodesFactory.PCallCapiFunctionNodeGen.getUncached();
- }
- }
-
- // -----------------------------------------------------------------------------------------------------------------
-
/**
* Use this method to lookup a native type member like {@code tp_alloc}.
*
@@ -890,7 +606,7 @@ public static PCallCapiFunction getUncached() {
*
*/
@TruffleBoundary
- public static Object lookupNativeMemberInMRO(PythonManagedClass cls, @SuppressWarnings("unused") CFields nativeMemberName, HiddenAttr managedMemberName) {
+ public static long lookupNativeMemberInMRO(PythonManagedClass cls, CFields nativeMemberName, HiddenAttr managedMemberName) {
NativeCAPISymbol symbol = null;
// We need to point to PyType_GenericAlloc or PyObject_GC_Del
if (managedMemberName == HiddenAttr.ALLOC) {
@@ -913,33 +629,33 @@ public static Object lookupNativeMemberInMRO(PythonManagedClass cls, @SuppressWa
symbol = FUN_NO_OP_CLEAR;
}
if (symbol != null) {
- Object func = HiddenAttr.ReadNode.executeUncached(cls, managedMemberName, null);
- if (func != null) {
+ long func = HiddenAttr.ReadLongNode.executeUncached(cls, managedMemberName, NULLPTR);
+ if (func != NULLPTR) {
return func;
}
- return CApiContext.getNativeSymbol(null, symbol);
+ return CApiContext.getNativeSymbol(null, symbol).getAddress();
}
MroSequenceStorage mroStorage = GetMroStorageNode.executeUncached(cls);
int n = mroStorage.length();
for (int i = 0; i < n; i++) {
PythonAbstractClass mroCls = (PythonAbstractClass) SequenceStorageNodes.GetItemDynamicNode.executeUncached(mroStorage, i);
if (PGuards.isManagedClass(mroCls)) {
- Object result = HiddenAttr.ReadNode.executeUncached((PythonObject) mroCls, managedMemberName, null);
- if (result != null) {
+ long result = HiddenAttr.ReadLongNode.executeUncached((PythonObject) mroCls, managedMemberName, NULLPTR);
+ if (result != NULLPTR) {
return result;
}
} else {
assert PGuards.isNativeClass(mroCls) : "invalid class inheritance structure; expected native class";
- Object result = CStructAccess.ReadPointerNode.getUncached().readFromObj((PythonNativeClass) mroCls, nativeMemberName);
- if (!PGuards.isNullOrZero(result, InteropLibrary.getUncached())) {
+ long result = CStructAccess.readPtrField(((PythonNativeClass) mroCls).getPtr(), nativeMemberName);
+ if (result != NULLPTR) {
return result;
}
}
}
if (managedMemberName == HiddenAttr.CLEAR && (TypeNodes.GetTypeFlagsNode.executeUncached(cls) & TypeFlags.HAVE_GC) != 0) {
- return CApiContext.getNativeSymbol(null, FUN_NO_OP_CLEAR);
+ return CApiContext.getNativeSymbol(null, FUN_NO_OP_CLEAR).getAddress();
}
- return HiddenAttr.ReadNode.executeUncached(PythonContext.get(null).lookupType(PythonBuiltinClassType.PythonObject), managedMemberName, NO_VALUE);
+ return HiddenAttr.ReadLongNode.executeUncached(PythonContext.get(null).lookupType(PythonBuiltinClassType.PythonObject), managedMemberName, NULLPTR);
}
/**
@@ -974,7 +690,7 @@ public static long lookupNativeI64MemberInMRO(Object cls, CFields nativeMemberNa
}
} else {
assert PGuards.isNativeClass(mroCls) : "invalid class inheritance structure; expected native class";
- return CStructAccess.ReadI64Node.getUncached().readFromObj((PythonNativeClass) mroCls, nativeMemberName);
+ return readLongField(((PythonNativeClass) mroCls).getPtr(), nativeMemberName);
}
}
// return the value from PyBaseObject - assumed to be 0 for vectorcall_offset
@@ -1005,7 +721,6 @@ static long doSingleContext(Object cls, CFields nativeMember, HiddenAttr managed
@Bind Node inliningTarget,
@Cached GetBaseClassNode getBaseClassNode,
@Cached HiddenAttr.ReadNode readAttrNode,
- @Cached CStructAccess.ReadI64Node getTypeMemberNode,
@Cached PyNumberAsSizeNode asSizeNode) {
CompilerAsserts.partialEvaluationConstant(builtinCallback);
@@ -1029,7 +744,7 @@ static long doSingleContext(Object cls, CFields nativeMember, HiddenAttr managed
}
} else {
assert PGuards.isNativeClass(current) : "invalid class inheritance structure; expected native class";
- return getTypeMemberNode.readFromObj((PythonNativeClass) current, nativeMember);
+ return readLongField(((PythonNativeClass) current).getPtr(), nativeMember);
}
current = getBaseClassNode.execute(inliningTarget, current);
} while (current != null);
@@ -1079,6 +794,15 @@ static Object doObject(Object errorValue, PythonBuiltinClassType errType, Truffl
return errorValue;
}
+ public static T raiseStatic(T errorValue, PythonBuiltinClassType errType, TruffleString format, Object... arguments) {
+ try {
+ throw PRaiseNode.raiseStatic(EncapsulatingNodeReference.getCurrent().get(), errType, format, arguments);
+ } catch (PException p) {
+ TransformPExceptionToNativeNode.executeUncached(p);
+ }
+ return errorValue;
+ }
+
private static void raiseNative(Node inliningTarget, PythonBuiltinClassType errType, TruffleString format, Object[] arguments, PRaiseNode raiseNode,
TransformPExceptionToNativeNode transformExceptionToNativeNode) {
try {
@@ -1111,173 +835,38 @@ static PRaiseNativeNode doIt(@Cached(inline = false) PRaiseNativeNode node) {
@GenerateCached(false)
@GenerateUncached
public abstract static class XDecRefPointerNode extends PNodeWithContext {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, FUN_PY_DEALLOC);
- public static void executeUncached(Object pointer) {
+ public static void executeUncached(long pointer) {
CExtNodesFactory.XDecRefPointerNodeGen.getUncached().execute(null, pointer);
}
- public abstract void execute(Node inliningTarget, Object pointer);
+ public abstract void execute(Node inliningTarget, long pointer);
@Specialization
- static void doDecref(Node inliningTarget, Object pointerObj,
- @CachedLibrary(limit = "2") InteropLibrary lib,
- @Cached(inline = false) CApiTransitions.ToPythonWrapperNode toPythonWrapperNode,
+ static void doDecref(Node inliningTarget, long pointer,
+ @Cached CApiTransitions.NativeToPythonInternalNode toPythonNode,
@Cached InlinedBranchProfile isWrapperProfile,
- @Cached InlinedBranchProfile isNativeObject,
- @Cached UpdateStrongRefNode updateRefNode,
- @Cached(inline = false) CStructAccess.ReadI64Node readRefcount,
- @Cached(inline = false) CStructAccess.WriteLongNode writeRefcount,
- @Cached(inline = false) PCallCapiFunction callDealloc) {
- long pointer;
- if (pointerObj instanceof Long longPointer) {
- pointer = longPointer;
- } else {
- if (lib.isPointer(pointerObj)) {
- try {
- pointer = lib.asPointer(pointerObj);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- } else {
- // No refcounting in managed mode
- return;
- }
- }
- if (pointer == 0) {
+ @Cached UpdateStrongRefNode updateRefNode) {
+ if (pointer == NULLPTR) {
return;
}
if (HandlePointerConverter.pointsToPyFloatHandle(pointer) || HandlePointerConverter.pointsToPyIntHandle(pointer)) {
return;
}
- PythonNativeWrapper wrapper = toPythonWrapperNode.executeWrapper(pointer, false);
- if (wrapper instanceof PythonAbstractObjectNativeWrapper objectWrapper) {
+ Object object = toPythonNode.execute(inliningTarget, pointer, false);
+ if (object instanceof PythonObject pythonObject) {
isWrapperProfile.enter(inliningTarget);
- updateRefNode.execute(inliningTarget, objectWrapper, objectWrapper.decRef());
- } else if (wrapper == null) {
- isNativeObject.enter(inliningTarget);
- assert NativeToPythonNode.executeUncached(new NativePointer(pointer)) instanceof PythonAbstractNativeObject;
- long refcount = readRefcount.read(pointer, PyObject__ob_refcnt);
- if (refcount != IMMORTAL_REFCNT) {
- refcount--;
- writeRefcount.write(pointer, PyObject__ob_refcnt, refcount);
- if (refcount == 0) {
- callDealloc.call(FUN_PY_DEALLOC, pointer);
- }
- }
+ updateRefNode.execute(inliningTarget, pythonObject, pythonObject.decRef());
} else {
- throw CompilerDirectives.shouldNotReachHere("Cannot DECREF non-object");
- }
- }
- }
-
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- @ImportStatic(PGuards.class)
- public abstract static class ClearNativeWrapperNode extends Node {
-
- public abstract void execute(Node inliningTarget, Object delegate, PythonNativeWrapper nativeWrapper);
-
- @Specialization(guards = "!isPrimitiveNativeWrapper(nativeWrapper)")
- static void doPythonAbstractObject(PythonAbstractObject delegate, PythonNativeWrapper nativeWrapper) {
- // For non-temporary wrappers (all wrappers that need to preserve identity):
- // If this assertion fails, it indicates that the native code still uses a free'd native
- // wrapper.
- // TODO(fa): explicitly mark native wrappers to be identity preserving
- assert !(nativeWrapper instanceof PythonObjectNativeWrapper) || delegate.getNativeWrapper() == nativeWrapper : "inconsistent native wrappers";
- delegate.clearNativeWrapper();
- }
-
- @Specialization(guards = "delegate == null")
- static void doPrimitiveNativeWrapper(Node inliningTarget, @SuppressWarnings("unused") Object delegate, PrimitiveNativeWrapper nativeWrapper) {
- // ignore
- }
-
- @Specialization(guards = "delegate != null")
- static void doPrimitiveNativeWrapperMaterialized(Node inliningTarget, PythonAbstractObject delegate, PrimitiveNativeWrapper nativeWrapper,
- @Cached InlinedConditionProfile profile) {
- if (profile.profile(inliningTarget, delegate.getNativeWrapper() == nativeWrapper)) {
- delegate.clearNativeWrapper();
- }
- }
-
- @Specialization(guards = {"delegate != null", "!isAnyPythonObject(delegate)"})
- static void doOther(@SuppressWarnings("unused") Object delegate, @SuppressWarnings("unused") PythonNativeWrapper nativeWrapper) {
- // ignore
- }
-
- static boolean isPrimitiveNativeWrapper(PythonNativeWrapper nativeWrapper) {
- return nativeWrapper instanceof PrimitiveNativeWrapper;
- }
- }
-
- @GenerateUncached
- @GenerateInline
- @GenerateCached(false)
- public abstract static class ResolvePointerNode extends PNodeWithContext {
-
- public static Object executeUncached(Object pointerObject) {
- return ResolvePointerNodeGen.getUncached().execute(null, pointerObject);
- }
-
- public abstract Object execute(Node inliningTarget, Object pointerObject);
-
- public abstract Object executeLong(Node inliningTarget, long pointer);
-
- @Specialization
- static Object resolveLongCached(Node inliningTarget, long pointer,
- @Exclusive @Cached ResolveHandleNode resolveHandleNode,
- @Exclusive @Cached UpdateStrongRefNode updateRefNode) {
- Object lookup = CApiTransitions.lookupNative(pointer);
- if (lookup != null) {
- if (lookup instanceof PythonAbstractObjectNativeWrapper objectNativeWrapper) {
- updateRefNode.execute(inliningTarget, objectNativeWrapper, objectNativeWrapper.incRef());
- }
- return lookup;
- }
- if (HandlePointerConverter.pointsToPyHandleSpace(pointer)) {
- if (HandlePointerConverter.pointsToPyIntHandle(pointer)) {
- return HandlePointerConverter.pointerToLong(pointer);
- } else if (HandlePointerConverter.pointsToPyFloatHandle(pointer)) {
- return HandlePointerConverter.pointerToDouble(pointer);
- }
- return resolveHandleNode.execute(inliningTarget, pointer);
- }
- return pointer;
- }
-
- @Specialization(guards = "!isLong(pointerObject)")
- static Object resolveGeneric(Node inliningTarget, Object pointerObject,
- @CachedLibrary(limit = "3") InteropLibrary lib,
- @Exclusive @Cached ResolveHandleNode resolveHandleNode,
- @Exclusive @Cached UpdateStrongRefNode updateRefNode) {
- if (lib.isPointer(pointerObject)) {
- Object lookup;
- long pointer;
- try {
- pointer = lib.asPointer(pointerObject);
- } catch (UnsupportedMessageException e) {
- throw shouldNotReachHere(e);
- }
- lookup = CApiTransitions.lookupNative(pointer);
- if (lookup != null) {
- if (lookup instanceof PythonAbstractObjectNativeWrapper objectNativeWrapper) {
- if (HandlePointerConverter.pointsToPyIntHandle(pointer)) {
- return HandlePointerConverter.pointerToLong(pointer);
- } else if (HandlePointerConverter.pointsToPyFloatHandle(pointer)) {
- return HandlePointerConverter.pointerToDouble(pointer);
- }
- updateRefNode.execute(inliningTarget, objectNativeWrapper, objectNativeWrapper.incRef());
- }
- return lookup;
- }
- if (HandlePointerConverter.pointsToPyHandleSpace(pointer)) {
- return resolveHandleNode.execute(inliningTarget, pointer);
+ assert object instanceof PythonAbstractNativeObject;
+ if (CApiTransitions.subNativeRefCount(pointer, 1) == 0) {
+ PythonContext context = PythonContext.get(inliningTarget);
+ var callable = CApiContext.getNativeSymbol(inliningTarget, FUN_PY_DEALLOC);
+ ExternalFunctionInvoker.invokePY_DEALLOC(null, C_API_TIMING, context.ensureNativeContext(), BoundaryCallData.getUncached(),
+ context.getThreadState(PythonLanguage.get(inliningTarget)), callable, pointer);
}
}
- // In this case, it cannot be a handle so we can just return the pointer object. It
- // could, of course, still be a native pointer.
- return pointerObject;
}
}
@@ -1351,11 +940,6 @@ public abstract static class ObSizeNode extends PNodeWithContext {
public abstract long execute(Node inliningTarget, Object object);
- @Specialization
- static long doPythonNativeVoidPtr(@SuppressWarnings("unused") PythonNativeVoidPtr object) {
- return ((Long.SIZE - 1) / PYLONG_BITS_IN_DIGIT.intValue() + 1);
- }
-
@Specialization
static long doClass(@SuppressWarnings("unused") PythonManagedClass object) {
return 0; // dummy value
@@ -1372,287 +956,13 @@ static long doOther(Node inliningTarget, Object object,
}
}
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- public abstract static class UnicodeFromFormatNode extends Node {
- private static Pattern pattern;
-
- public static Object executeUncached(TruffleString format, Object vaList) {
- return UnicodeFromFormatNodeGen.getUncached().execute(null, format, vaList);
- }
-
- private static Matcher match(String formatStr) {
- if (pattern == null) {
- pattern = Pattern.compile("%(?[-+ #0])?(?\\d+)?(\\.(?\\d+))?(?(l|ll|z))?(?[%cduixspAUVSR])");
- }
- return pattern.matcher(formatStr);
- }
-
- public abstract Object execute(Node inliningTarget, TruffleString format, Object vaList);
-
- @Specialization
- @TruffleBoundary
- Object doGeneric(TruffleString f, Object vaList) {
- // TODO use TruffleString [GR-38103]
- String format = f.toJavaStringUncached();
-
- // helper nodes
- NativeToPythonNode toJavaNode = NativeToPythonNodeGen.getUncached();
- CastToJavaStringNode castToJavaStringNode = CastToJavaStringNodeGen.getUncached();
- FromCharPointerNode fromCharPointerNode = FromCharPointerNodeGen.getUncached();
- InteropLibrary interopLibrary = InteropLibrary.getUncached();
-
- StringBuilder result = new StringBuilder();
- int vaArgIdx = 0;
- Object unicodeObj;
- try {
- Matcher matcher = match(format);
- int cur = 0;
- while (matcher.find(cur)) {
- // not all combinations are valid
- boolean valid = false;
-
- // add anything before the match
- result.append(format, cur, matcher.start());
-
- cur = matcher.end();
-
- String spec = matcher.group("spec");
- String len = matcher.group("len");
- int prec = getPrec(matcher.group("prec"));
- assert spec.length() == 1;
- char la = spec.charAt(0);
- PythonContext context = PythonContext.get(null);
- switch (la) {
- case '%':
- // %%
- result.append('%');
- valid = true;
- break;
- case 'c':
- int ordinal = getAndCastToInt(interopLibrary, vaList);
- if (ordinal < 0 || ordinal > 0x110000) {
- throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.OverflowError, ErrorMessages.CHARACTER_ARG_NOT_IN_RANGE);
- }
- result.append((char) ordinal);
- vaArgIdx++;
- valid = true;
- break;
- case 'd':
- case 'i':
- // %d, %i, %ld, %li, %lld, %lli, %zd, %zi
- if (len != null) {
- switch (len) {
- case "ll":
- case "l":
- case "z":
- vaArgIdx++;
- result.append(castToLong(interopLibrary, GetNextVaArgNode.executeUncached(vaList)));
- valid = true;
- break;
- }
- } else {
- result.append(getAndCastToInt(interopLibrary, vaList));
- vaArgIdx++;
- valid = true;
- }
- break;
- case 'u':
- // %u, %lu, %llu, %zu
- if (len != null) {
- switch (len) {
- case "ll":
- case "l":
- case "z":
- vaArgIdx++;
- result.append(castToLong(interopLibrary, GetNextVaArgNode.executeUncached(vaList)));
- valid = true;
- break;
- }
- } else {
- result.append(Integer.toUnsignedString(getAndCastToInt(interopLibrary, vaList)));
- vaArgIdx++;
- valid = true;
- }
- break;
- case 'x':
- // %x
- result.append(Integer.toHexString(getAndCastToInt(interopLibrary, vaList)));
- vaArgIdx++;
- valid = true;
- break;
- case 's':
- // %s
- Object charPtr = GetNextVaArgNode.executeUncached(vaList);
- String sValue;
- if (interopLibrary.isNull(charPtr)) {
- // CPython would segfault. Let's make debugging easier for ourselves
- sValue = "(NULL)";
- } else {
- unicodeObj = fromCharPointerNode.execute(charPtr);
- sValue = castToJavaStringNode.execute(unicodeObj);
- }
- try {
- if (prec == -1) {
- result.append(sValue);
- } else {
- result.append(sValue, 0, Math.min(sValue.length(), prec));
- }
- } catch (CannotCastException e) {
- // That should really not happen because we created the unicode
- // object with FromCharPointerNode which guarantees to return a
- // String/PString.
- throw shouldNotReachHere();
- }
- vaArgIdx++;
- valid = true;
- break;
- case 'p':
- // %p
- Object ptr = GetNextVaArgNode.executeUncached(vaList);
- long value;
- if (interopLibrary.isPointer(ptr)) {
- value = interopLibrary.asPointer(ptr);
- } else if (interopLibrary.hasIdentity(ptr)) {
- value = interopLibrary.identityHashCode(ptr);
- } else {
- value = System.identityHashCode(ptr);
- }
- result.append(PythonUtils.formatJString("0x%x", value));
- vaArgIdx++;
- valid = true;
- break;
- case 'A':
- // %A
- result.append(callBuiltin(context, BuiltinNames.T_ASCII, getPyObject(vaList)));
- vaArgIdx++;
- valid = true;
- break;
- case 'U':
- // %U
- result.append(castToJavaStringNode.execute(getPyObject(vaList)));
- vaArgIdx++;
- valid = true;
- break;
- case 'V':
- // %V
- Object pyObjectPtr = GetNextVaArgNode.executeUncached(vaList);
- if (InteropLibrary.getUncached().isNull(pyObjectPtr)) {
- unicodeObj = fromCharPointerNode.execute(GetNextVaArgNode.executeUncached(vaList));
- } else {
- unicodeObj = toJavaNode.execute(pyObjectPtr);
- }
- result.append(castToJavaStringNode.execute(unicodeObj));
- vaArgIdx += 2;
- valid = true;
- break;
- case 'S':
- // %S
- result.append(callBuiltin(context, BuiltinNames.T_STR, getPyObject(vaList)));
- vaArgIdx++;
- valid = true;
- break;
- case 'R':
- // %R
- result.append(callBuiltin(context, BuiltinNames.T_REPR, getPyObject(vaList)));
- vaArgIdx++;
- valid = true;
- break;
- }
- // this means, we did not detect a valid format specifier, so add the whole
- // group
- if (!valid) {
- result.append(matcher.group());
- }
- }
- // add anything after the last matched group (or the whole format string if nothing
- // matched)
- result.append(format, cur, format.length());
- } catch (InteropException e) {
- throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.SystemError, ErrorMessages.ERROR_WHEN_ACCESSING_VAR_ARG_AT_POS, vaArgIdx);
- }
- return toTruffleStringUncached(result.toString());
- }
-
- private static int getPrec(String prec) {
- if (prec == null) {
- return -1;
- }
- return Integer.parseInt(prec);
- }
-
- /**
- * Read an element from the {@code va_list} with the specified type and cast it to a Java
- * {@code int}. Throws a {@code SystemError} if this is not possible.
- */
- private int getAndCastToInt(InteropLibrary lib, Object vaList) throws InteropException {
- Object value = GetNextVaArgNode.executeUncached(vaList);
- if (lib.fitsInInt(value)) {
- try {
- return lib.asInt(value);
- } catch (UnsupportedMessageException e) {
- throw shouldNotReachHere();
- }
- }
- if (!lib.isPointer(value)) {
- lib.toNative(value);
- }
- if (lib.isPointer(value)) {
- try {
- return (int) lib.asPointer(value);
- } catch (UnsupportedMessageException e) {
- throw shouldNotReachHere();
- }
- }
- throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.SystemError, ErrorMessages.P_OBJ_CANT_BE_INTEPRETED_AS_INTEGER, value);
- }
-
- /**
- * Cast a value to a Java {@code long}. Throws a {@code SystemError} if this is not
- * possible.
- */
- private long castToLong(InteropLibrary lib, Object value) {
- if (lib.fitsInLong(value)) {
- try {
- return lib.asLong(value);
- } catch (UnsupportedMessageException e) {
- throw shouldNotReachHere();
- }
- }
- if (!lib.isPointer(value)) {
- lib.toNative(value);
- }
- if (lib.isPointer(value)) {
- try {
- return lib.asPointer(value);
- } catch (UnsupportedMessageException e) {
- throw shouldNotReachHere();
- }
- }
- throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.SystemError, ErrorMessages.P_OBJ_CANT_BE_INTEPRETED_AS_INTEGER, value);
- }
-
- private static Object getPyObject(Object vaList) throws InteropException {
- return NativeToPythonNode.executeUncached(GetNextVaArgNode.executeUncached(vaList));
- }
-
- @TruffleBoundary
- private static Object callBuiltin(PythonContext context, TruffleString builtinName, Object object) {
- Object attribute = PyObjectLookupAttr.executeUncached(context.getBuiltins(), builtinName);
- return CastToJavaStringNodeGen.getUncached().execute(CallNode.executeUncached(attribute, object));
- }
- }
-
// according to definitions in 'moduleobject.h'
private static final int SLOT_PY_MOD_CREATE = 1;
private static final int SLOT_PY_MOD_EXEC = 2;
private static final int SLOT_PY_MOD_MULTIPLE_INTERPRETERS = 3;
- private static final String NFI_CREATE_NAME = "create";
- private static final String NFI_CREATE_SRC = "(POINTER,POINTER):POINTER";
- private static final Source NFI_LIBFFI_CREATE = Source.newBuilder(J_NFI_LANGUAGE, NFI_CREATE_SRC, NFI_CREATE_NAME).build();
- private static final Source NFI_PANAMA_CREATE = Source.newBuilder(J_NFI_LANGUAGE, "with panama " + NFI_CREATE_SRC, NFI_CREATE_NAME).build();
+ private static final CApiTiming TIMING_MOD_CREATE = CApiTiming.create(true, "Py_mod_create");
+ private static final CApiTiming TIMING_MOD_EXEC = CApiTiming.create(true, "Py_mod_exec");
/**
* Equivalent of {@code PyModule_FromDefAndSpec}. Creates a Python module from a module
@@ -1673,11 +983,7 @@ private static Object callBuiltin(PythonContext context, TruffleString builtinNa
*
*/
@TruffleBoundary
- static Object createModule(Node node, CApiContext capiContext, ModuleSpec moduleSpec, Object moduleDefWrapper, Object library) {
- InteropLibrary interopLib = InteropLibrary.getUncached();
- // call to type the pointer
- Object moduleDef = moduleDefWrapper instanceof PythonAbstractNativeObject ? ((PythonAbstractNativeObject) moduleDefWrapper).getPtr() : moduleDefWrapper;
-
+ static Object createModule(Node node, CApiContext capiContext, ModuleSpec moduleSpec, long moduleDefPtr, Object library) {
/*
* The name of the module is taken from the module spec and *NOT* from the module
* definition.
@@ -1686,34 +992,34 @@ static Object createModule(Node node, CApiContext capiContext, ModuleSpec module
Object mDoc;
long mSize;
// do not eagerly read the doc string; this turned out to be unnecessarily expensive
- Object docPtr = CStructAccess.ReadPointerNode.readUncached(moduleDef, PyModuleDef__m_doc);
- if (PGuards.isNullOrZero(docPtr, interopLib)) {
+ long docPtr = readPtrField(moduleDefPtr, PyModuleDef__m_doc);
+ if (docPtr == NULLPTR) {
mDoc = NO_VALUE;
} else {
mDoc = FromCharPointerNode.executeUncached(docPtr);
}
- mSize = CStructAccess.ReadI64Node.getUncached().read(moduleDef, PyModuleDef__m_size);
+ mSize = readLongField(moduleDefPtr, PyModuleDef__m_size);
if (mSize < 0) {
throw PRaiseNode.raiseStatic(node, PythonBuiltinClassType.SystemError, ErrorMessages.M_SIZE_CANNOT_BE_NEGATIVE, mName);
}
// parse slot definitions
- Object createFunction = null;
+ long createFunction = NULLPTR;
boolean hasExecutionSlots = false;
- Object slotDefinitions = CStructAccess.ReadPointerNode.readUncached(moduleDef, PyModuleDef__m_slots);
- if (!interopLib.isNull(slotDefinitions)) {
+ long slotDefinitions = readPtrField(moduleDefPtr, PyModuleDef__m_slots);
+ if (slotDefinitions != NULLPTR) {
loop: for (int i = 0;; i++) {
- int slotId = CStructAccess.ReadI32Node.getUncached().readStructArrayElement(slotDefinitions, i, PyModuleDef_Slot__slot);
+ int slotId = readStructArrayIntField(slotDefinitions, i, PyModuleDef_Slot__slot);
switch (slotId) {
case 0:
break loop;
case SLOT_PY_MOD_CREATE:
- if (createFunction != null) {
+ if (createFunction != NULLPTR) {
throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_HAS_MULTIPLE_CREATE_SLOTS, mName);
}
- createFunction = CStructAccess.ReadPointerNode.getUncached().readStructArrayElement(slotDefinitions, i, PyModuleDef_Slot__value);
+ createFunction = readStructArrayPtrField(slotDefinitions, i, PyModuleDef_Slot__value);
break;
case SLOT_PY_MOD_EXEC:
hasExecutionSlots = true;
@@ -1730,24 +1036,15 @@ static Object createModule(Node node, CApiContext capiContext, ModuleSpec module
PythonContext context = capiContext.getContext();
Object module;
- if (createFunction != null && !interopLib.isNull(createFunction)) {
- Object[] cArguments = new Object[]{PythonToNativeNode.executeUncached(moduleSpec.originalModuleSpec), moduleDef};
- try {
- Object result;
- if (!interopLib.isExecutable(createFunction)) {
- boolean panama = context.getOption(PythonOptions.UsePanama);
- Object signature = context.getEnv().parseInternal(panama ? NFI_PANAMA_CREATE : NFI_LIBFFI_CREATE).call();
- result = interopLib.execute(SignatureLibrary.getUncached().bind(signature, createFunction), cArguments);
- } else {
- result = interopLib.execute(createFunction, cArguments);
- }
- PythonThreadState threadState = context.getThreadState(context.getLanguage());
- TransformExceptionFromNativeNode.getUncached().execute(null, threadState, mName, interopLib.isNull(result), true,
- ErrorMessages.CREATION_FAILD_WITHOUT_EXCEPTION, ErrorMessages.CREATION_RAISED_EXCEPTION);
- module = NativeToPythonTransferNode.executeUncached(result);
- } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
- throw shouldNotReachHere(e);
- }
+ if (createFunction != NULLPTR) {
+ PythonThreadState threadState = context.getThreadState(context.getLanguage());
+ NativeFunctionPointer modCreate = ExternalFunctionSignature.MODCREATE.bind(context.ensureNativeContext(), createFunction);
+ long result = ExternalFunctionInvoker.invokeMODCREATE(null, TIMING_MOD_CREATE, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(), threadState, modCreate,
+ PythonToNativeInternalNode.executeUncached(moduleSpec.originalModuleSpec, false), moduleDefPtr);
+ TransformExceptionFromNativeNode.getUncached().execute(null, threadState, mName, result == NULLPTR, true,
+ ErrorMessages.CREATION_FAILD_WITHOUT_EXCEPTION, ErrorMessages.CREATION_RAISED_EXCEPTION);
+ module = NativeToPythonTransferNode.executeRawUncached(result);
/*
* We are more strict than CPython and require this to be a PythonModule object. This
@@ -1763,16 +1060,16 @@ static Object createModule(Node node, CApiContext capiContext, ModuleSpec module
}
// otherwise CPython is just fine
} else {
- ((PythonModule) module).setNativeModuleDef(moduleDef);
+ ((PythonModule) module).setNativeModuleDef(moduleDefPtr);
}
} else {
PythonModule pythonModule = PFactory.createPythonModule(mName);
- pythonModule.setNativeModuleDef(moduleDef);
+ pythonModule.setNativeModuleDef(moduleDefPtr);
module = pythonModule;
}
- Object methodDefinitions = CStructAccess.ReadPointerNode.readUncached(moduleDef, PyModuleDef__m_methods);
- if (!interopLib.isNull(methodDefinitions)) {
+ long methodDefinitions = readPtrField(moduleDefPtr, PyModuleDef__m_methods);
+ if (methodDefinitions != NULLPTR) {
for (int i = 0;; i++) {
PBuiltinFunction fun = createLegacyMethod(methodDefinitions, i, context.getLanguage());
if (fun == null) {
@@ -1789,86 +1086,73 @@ static Object createModule(Node node, CApiContext capiContext, ModuleSpec module
return module;
}
- private static final String NFI_EXEC_SRC = "(POINTER):SINT32";
- private static final Source NFI_LIBFFI_EXEC = Source.newBuilder(J_NFI_LANGUAGE, NFI_EXEC_SRC, "exec").build();
- private static final Source NFI_PANAMA_EXEC = Source.newBuilder(J_NFI_LANGUAGE, "with panama " + NFI_EXEC_SRC, "exec").build();
-
/**
* Equivalent of {@code PyModule_ExecDef}.
*/
@TruffleBoundary
- public static int execModule(Node node, CApiContext capiContext, PythonModule module, Object moduleDef) {
- InteropLibrary interopLib = InteropLibrary.getUncached();
- // call to type the pointer
-
+ public static int execModule(Node node, CApiContext capiContext, PythonModule module, long moduleDef) {
TruffleString mName = ModuleGetNameNode.executeUncached(module);
- long mSize = CStructAccess.ReadI64Node.getUncached().read(moduleDef, PyModuleDef__m_size);
-
- try {
- // allocate md_state if necessary
- if (mSize >= 0) {
- /*
- * TODO(fa): We currently leak 'md_state' and need to use a shared finalizer or
- * similar. We ignore that for now since the size will usually be very small and/or
- * we could also use a Truffle buffer object.
- */
- Object mdState = CStructAccess.AllocateNode.allocUncached(mSize == 0 ? 1 : mSize); // ensure
- // non-null
- // value
- assert mdState != null && !InteropLibrary.getUncached().isNull(mdState);
- module.setNativeModuleState(mdState);
- }
+ long mSize = readLongField(moduleDef, PyModuleDef__m_size);
- // parse slot definitions
- Object slotDefinitions = CStructAccess.ReadPointerNode.readUncached(moduleDef, PyModuleDef__m_slots);
- if (interopLib.isNull(slotDefinitions)) {
- return 0;
- }
- loop: for (int i = 0;; i++) {
- int slotId = CStructAccess.ReadI32Node.getUncached().readStructArrayElement(slotDefinitions, i, PyModuleDef_Slot__slot);
- switch (slotId) {
- case 0:
- break loop;
- case SLOT_PY_MOD_CREATE:
- // handled in CreateModuleNode
- break;
- case SLOT_PY_MOD_EXEC:
- Object execFunction = CStructAccess.ReadPointerNode.getUncached().readStructArrayElement(slotDefinitions, i, PyModuleDef_Slot__value);
- PythonContext context = capiContext.getContext();
- if (!interopLib.isExecutable(execFunction)) {
- boolean panama = context.getOption(PythonOptions.UsePanama);
- Object signature = context.getEnv().parseInternal(panama ? NFI_PANAMA_EXEC : NFI_LIBFFI_EXEC).call();
- execFunction = SignatureLibrary.getUncached().bind(signature, execFunction);
- }
- Object result = interopLib.execute(execFunction, PythonToNativeNode.executeUncached(module));
- int iResult = interopLib.asInt(result);
- /*
- * It's a bit counterintuitive that we use 'isPrimitiveValue = false' but
- * the function's return value is actually not a result but a status code.
- * So, if the status code is '!=0' we know that an error occurred and won't
- * ignore this if no error is set. This is then the same behaviour if we
- * would have a pointer return type and got 'NULL'.
- */
- PythonThreadState threadState = context.getThreadState(context.getLanguage());
- TransformExceptionFromNativeNode.getUncached().execute(node, threadState, mName, iResult != 0, true,
- ErrorMessages.EXECUTION_FAILED_WITHOUT_EXCEPTION, ErrorMessages.EXECUTION_RAISED_EXCEPTION);
- break;
- case SLOT_PY_MOD_MULTIPLE_INTERPRETERS:
- // ignored
- // (mq) TODO: handle multiple interpreter cases
- break;
- default:
- throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_INITIALIZED_WITH_UNKNOWN_SLOT, mName, slotId);
- }
+ // allocate md_state if necessary
+ if (mSize >= 0) {
+ /*
+ * TODO(fa): We currently leak 'md_state' and need to use a shared finalizer or similar.
+ * We ignore that for now since the size will usually be very small and/or we could also
+ * use a Truffle buffer object.
+ */
+ long mdState = calloc(mSize == 0 ? 1 : mSize); // ensure non-null value
+ assert mdState != NULLPTR;
+ module.setNativeModuleState(mdState);
+ }
+
+ // parse slot definitions
+ long slotDefinitions = readPtrField(moduleDef, PyModuleDef__m_slots);
+ if (slotDefinitions == NULLPTR) {
+ return 0;
+ }
+ loop: for (int i = 0;; i++) {
+ int slotId = readStructArrayIntField(slotDefinitions, i, PyModuleDef_Slot__slot);
+ switch (slotId) {
+ case 0:
+ break loop;
+ case SLOT_PY_MOD_CREATE:
+ // handled in CreateModuleNode
+ break;
+ case SLOT_PY_MOD_EXEC:
+ long execFunction = readStructArrayPtrField(slotDefinitions, i, PyModuleDef_Slot__value);
+ PythonContext context = capiContext.getContext();
+ PythonThreadState threadState = context.getThreadState(context.getLanguage());
+ NativeFunctionPointer boundFunction = ExternalFunctionSignature.MODEXEC.bind(context.ensureNativeContext(), execFunction);
+ int iResult = ExternalFunctionInvoker.invokeMODEXEC(null, TIMING_MOD_EXEC, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(), threadState, boundFunction,
+ PythonToNativeInternalNode.executeUncached(module, false));
+ /*
+ * It's a bit counterintuitive that we use 'isPrimitiveValue = false' but the
+ * function's return value is actually not a result but a status code. So, if
+ * the status code is '!=0' we know that an error occurred and won't ignore this
+ * if no error is set. This is then the same behaviour if we would have a
+ * pointer return type and got 'NULL'.
+ */
+ TransformExceptionFromNativeNode.getUncached().execute(node, threadState, mName, iResult != 0, true,
+ ErrorMessages.EXECUTION_FAILED_WITHOUT_EXCEPTION, ErrorMessages.EXECUTION_RAISED_EXCEPTION);
+ break;
+ case SLOT_PY_MOD_MULTIPLE_INTERPRETERS:
+ // ignored
+ // (mq) TODO: handle multiple interpreter cases
+ break;
+ default:
+ throw PRaiseNode.raiseStatic(node, SystemError, ErrorMessages.MODULE_INITIALIZED_WITH_UNKNOWN_SLOT, mName, slotId);
}
- } catch (UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
- throw shouldNotReachHere();
}
return 0;
}
/**
+ * TODO(fa): overlaps with
+ * {@link com.oracle.graal.python.builtins.modules.cext.PythonCextModuleBuiltins#GraalPyPrivate_Module_AddFunctions(long, long)}.
+ *
*
* struct PyMethodDef {
* const char * ml_name;
@@ -1879,34 +1163,33 @@ public static int execModule(Node node, CApiContext capiContext, PythonModule mo
*
*/
@TruffleBoundary
- static PBuiltinFunction createLegacyMethod(Object methodDef, int element, PythonLanguage language) {
- InteropLibrary interopLib = InteropLibrary.getUncached();
- Object methodNamePtr = CStructAccess.ReadPointerNode.getUncached().readStructArrayElement(methodDef, element, PyMethodDef__ml_name);
- if (interopLib.isNull(methodNamePtr) || (methodNamePtr instanceof Long && ((long) methodNamePtr) == 0)) {
+ static PBuiltinFunction createLegacyMethod(long methodDefPtr, int element, PythonLanguage language) {
+ long methodNamePtr = readStructArrayPtrField(methodDefPtr, element, PyMethodDef__ml_name);
+ if (methodNamePtr == NULLPTR) {
return null;
}
TruffleString methodName = FromCharPointerNode.executeUncached(methodNamePtr);
// note: 'ml_doc' may be NULL; in this case, we would store 'None'
Object methodDoc = PNone.NONE;
- Object methodDocPtr = CStructAccess.ReadPointerNode.getUncached().readStructArrayElement(methodDef, element, PyMethodDef__ml_doc);
- if (!interopLib.isNull(methodDocPtr)) {
+ long methodDocPtr = readStructArrayPtrField(methodDefPtr, element, PyMethodDef__ml_doc);
+ if (methodDocPtr != NULLPTR) {
methodDoc = FromCharPointerNode.executeUncached(methodDocPtr, false);
}
- int flags = CStructAccess.ReadI32Node.getUncached().readStructArrayElement(methodDef, element, PyMethodDef__ml_flags);
- Object mlMethObj = CStructAccess.ReadPointerNode.getUncached().readStructArrayElement(methodDef, element, PyMethodDef__ml_meth);
+ int flags = readStructArrayIntField(methodDefPtr, element, PyMethodDef__ml_flags);
+ long mlMethObj = readStructArrayPtrField(methodDefPtr, element, PyMethodDef__ml_meth);
// CPy-style methods
// TODO(fa) support static and class methods
- PExternalFunctionWrapper sig = PExternalFunctionWrapper.fromMethodFlags(flags);
- RootCallTarget callTarget = PExternalFunctionWrapper.getOrCreateCallTarget(sig, language, methodName, CExtContext.isMethStatic(flags));
- mlMethObj = EnsureExecutableNode.executeUncached(mlMethObj, sig);
- PKeyword[] kwDefaults = ExternalFunctionNodes.createKwDefaults(mlMethObj);
+ MethodDescriptorWrapper sig = MethodDescriptorWrapper.fromMethodFlags(flags);
+ RootCallTarget callTarget = MethodDescriptorWrapper.getOrCreateCallTarget(language, sig, methodName, CExtContext.isMethStatic(flags));
+ NativeFunctionPointer fun = CExtCommonNodes.bindFunctionPointer(mlMethObj, sig);
+ PKeyword[] kwDefaults = ExternalFunctionNodes.createKwDefaults(fun);
PBuiltinFunction function = PFactory.createBuiltinFunction(language, methodName, null, PythonUtils.EMPTY_OBJECT_ARRAY, kwDefaults, flags, callTarget);
- HiddenAttr.WriteNode.executeUncached(function, METHOD_DEF_PTR, methodDef);
+ HiddenAttr.WriteLongNode.executeUncached(function, METHOD_DEF_PTR, methodDefPtr);
// write doc string; we need to directly write to the storage otherwise it is disallowed
// writing to builtin types.
- WriteAttributeToPythonObjectNode.getUncached().execute(function, SpecialAttributeNames.T___DOC__, methodDoc);
+ WriteAttributeToPythonObjectNode.executeUncached(function, SpecialAttributeNames.T___DOC__, methodDoc);
return function;
}
@@ -1918,13 +1201,10 @@ public abstract static class HasNativeBufferNode extends PNodeWithContext {
public abstract boolean execute(Node inliningTarget, PythonAbstractNativeObject object);
@Specialization
- static boolean readTpAsBuffer(PythonAbstractNativeObject object,
- @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached(inline = false) CStructAccess.ReadPointerNode readType,
- @Cached(inline = false) CStructAccess.ReadPointerNode readAsBuffer) {
- Object type = readType.readFromObj(object, PyObject__ob_type);
- Object result = readAsBuffer.read(type, PyTypeObject__tp_as_buffer);
- return !PGuards.isNullOrZero(result, lib);
+ static boolean readTpAsBuffer(PythonAbstractNativeObject object) {
+ long type = readPtrField(object.getPtr(), PyObject__ob_type);
+ long result = readPtrField(type, PyTypeObject__tp_as_buffer);
+ return result != NULLPTR;
}
}
@@ -1932,93 +1212,98 @@ static boolean readTpAsBuffer(PythonAbstractNativeObject object,
@GenerateCached(false)
@GenerateUncached
public abstract static class CreateMemoryViewFromNativeNode extends PNodeWithContext {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, FUN_GRAALPY_MEMORYVIEW_FROM_OBJECT);
+
public abstract PMemoryView execute(Node inliningTarget, PythonNativeObject object, int flags);
@Specialization
static PMemoryView fromNative(PythonNativeObject buf, int flags,
- @Cached(inline = false) PythonToNativeNode toSulongNode,
+ @Bind Node inliningTarget,
+ @Cached(inline = false) PythonToNativeNode toNativeNode,
@Cached(inline = false) NativeToPythonTransferNode asPythonObjectNode,
- @Cached(inline = false) PCallCapiFunction callCapiFunction,
- @Cached(inline = false) DefaultCheckFunctionResultNode checkFunctionResultNode) {
- Object result = callCapiFunction.call(FUN_GRAALPY_MEMORYVIEW_FROM_OBJECT, toSulongNode.execute(buf), flags);
- checkFunctionResultNode.execute(PythonContext.get(callCapiFunction), FUN_GRAALPY_MEMORYVIEW_FROM_OBJECT.getTsName(), result);
- return (PMemoryView) asPythonObjectNode.execute(result);
+ @Cached(inline = false) PyObjectCheckFunctionResultNode checkFunctionResultNode) {
+ long bufPointer = toNativeNode.executeLong(buf);
+ try {
+ PythonContext context = PythonContext.get(inliningTarget);
+ var callable = CApiContext.getNativeSymbol(inliningTarget, FUN_GRAALPY_MEMORYVIEW_FROM_OBJECT);
+ long result = ExternalFunctionInvoker.invokeGRAALPY_MEMORYVIEW_FROM_OBJECT(null, C_API_TIMING, context.ensureNativeContext(), BoundaryCallData.getUncached(),
+ context.getThreadState(PythonLanguage.get(inliningTarget)), callable, bufPointer, flags);
+ return (PMemoryView) checkFunctionResultNode.execute(context, FUN_GRAALPY_MEMORYVIEW_FROM_OBJECT.getTsName(), asPythonObjectNode.executeRaw(result));
+ } finally {
+ Reference.reachabilityFence(buf);
+ }
}
}
/**
- * Decrements the ref count by one of any {@link PythonNativeWrapper} object.
- *
- * This node avoids memory leaks for arguments given to native.
- * Problem description:
- * {@link PythonNativeWrapper} objects given to C code may go to native, i.e., a handle will be
- * allocated. In this case, no ref count manipulation is done since the C code considers the
- * reference to be borrowed and the Python code just doesn't do it because we have a GC. This
- * means that the handle will stay allocated and we are leaking the wrapper object.
- *
+ * Special helper node that promotes primitive values to {@link PythonObject} such that they can
+ * be connected with a native companion.
*/
@GenerateInline(false)
- @ImportStatic(CApiGuards.class)
- abstract static class ReleaseNativeWrapperNode extends Node {
+ @GenerateUncached
+ @ImportStatic({PGuards.class, CApiContext.class, PythonToNativeInternalNode.class})
+ public abstract static class EnsurePythonObjectNode extends Node {
- public abstract void execute(Object pythonObject);
+ @TruffleBoundary
+ public static PythonAbstractObject executeUncached(PythonContext context, Object object) {
+ return (PythonAbstractObject) EnsurePythonObjectNodeGen.getUncached().execute(context, object, true);
+ }
- @Specialization
- static void doNativeWrapper(@SuppressWarnings("unused") PythonAbstractObjectNativeWrapper nativeWrapper) {
- /*
- * TODO(fa): this is the place where we should decrease the wrapper's refcount by 1 and
- * also make the ref weak
- */
+ @TruffleBoundary
+ public static boolean doesNotNeedPromotion(Object object) {
+ return EnsurePythonObjectNodeGen.getUncached().execute(PythonContext.get(null), object, false) == object;
}
- @Specialization(guards = "!isNativeWrapper(object)")
- @SuppressWarnings("unused")
- static void doOther(Object object) {
- // just do nothing; this is an implicit profile
+ @TruffleBoundary
+ public static Object executeUncached(PythonContext context, Object object, boolean promoteBoxable) {
+ return EnsurePythonObjectNodeGen.getUncached().execute(context, object, promoteBoxable);
}
- }
- /**
- * Similar to CPython's macro {@code Py_VISIT}, this node will call the provided visit function
- * on the item if that item is a native object. This is because we assume that the traverse and
- * visit functions are only used in the Python GC to determine reference cycles due to reference
- * counting. If a reference cycle is interrupted by a managed reference, we are fine
- * because the Java GC will correctly handle that.
- */
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- public abstract static class VisitNode extends Node {
-
- /**
- * Calls the visit function on the given item.
- *
- * @param frame The virtual frame (may be {code null}).
- * @param inliningTarget The inlining target.
- * @param threadState The Python thread state (must not be {@code null}).
- * @param item The item to visit (may be {@code null}). Only a native object will be
- * visited.
- * @param visitFunction The visit function to call. This is expected to be an
- * {@link InteropLibrary#isExecutable(Object) executable} interop object (must
- * not be {@code null}).
- * @param visitArg The argument for the visit function as provided by the root caller.
- * @return {@code 0} on success, {@code !=0} on error
- */
- public abstract int execute(VirtualFrame frame, Node inliningTarget, PythonThreadState threadState, Object item, Object visitFunction, Object visitArg);
+ public abstract Object execute(PythonContext context, Object object, boolean promoteBoxable);
@Specialization
- static int doGeneric(VirtualFrame frame, Node inliningTarget, PythonThreadState threadState, Object item, Object visitFunction, Object visitArg,
- @Cached InlinedConditionProfile isNativeObjectProfile,
- @Cached ExternalFunctionInvokeNode externalFunctionInvokeNode,
- @Cached(inline = false) CheckPrimitiveFunctionResultNode checkPrimitiveFunctionResultNode,
- @Cached(inline = false) PythonToNativeNode toNativeNode) {
- assert InteropLibrary.getUncached().isExecutable(visitFunction);
- if (isNativeObjectProfile.profile(inliningTarget, item instanceof PythonAbstractNativeObject)) {
- Object result = externalFunctionInvokeNode.call(frame, inliningTarget, threadState, CApiGCSupport.VISIT_TIMING, StringLiterals.T_VISIT, visitFunction,
- toNativeNode.execute(item), visitArg);
- return (int) checkPrimitiveFunctionResultNode.executeLong(threadState, StringLiterals.T_VISIT, result);
+ static Object doGeneric(PythonContext context, Object obj, boolean promoteBoxable,
+ @Bind Node inliningTarget,
+ @Cached InlinedExactClassProfile classProfile,
+ @Cached GetClassNode getClassNode) {
+ CompilerAsserts.partialEvaluationConstant(promoteBoxable);
+
+ Object profiled = classProfile.profile(inliningTarget, obj);
+ if (profiled instanceof PythonObject pythonObject) {
+ return pythonObject;
+ } else if (profiled instanceof Integer i) {
+ return promoteBoxable ? PFactory.createInt(context.getLanguage(), i) : i;
+ } else if (profiled instanceof Long l) {
+ return promoteBoxable || !PInt.fitsInInt(l) ? PFactory.createInt(context.getLanguage(), l) : l;
+ } else if (profiled instanceof Float f) {
+ return promoteBoxable ? PFactory.createFloat(context.getLanguage(), f) : f;
+ } else if (profiled instanceof Double d) {
+ return promoteBoxable || !PFloat.fitsInFloat(d) ? PFactory.createFloat(context.getLanguage(), d) : d;
+ } else if (profiled instanceof Boolean b) {
+ return b ? context.getTrue() : context.getFalse();
+ } else if (profiled instanceof TruffleString s) {
+ return PFactory.createString(context.getLanguage(), s);
+ } else if (profiled instanceof PythonBuiltinClassType pbct) {
+ return context.lookupType(pbct);
+ } else if (CApiContext.isSpecialSingleton(profiled) || profiled instanceof PythonAbstractNativeObject || PythonToNativeInternalNode.mapsToNull(profiled)) {
+ return profiled;
+ } else if (PGuards.isForeignObject(profiled)) {
+ assert profiled != null : "attempting to wrap Java null";
+ Object clazz = getClassNode.execute(inliningTarget, profiled);
+ return PFactory.createPythonForeignObject(context.getLanguage(), clazz, profiled);
}
- return 0;
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ throw CompilerDirectives.shouldNotReachHere("unexpected object for promotion: " + profiled);
+ }
+
+ @NeverDefault
+ public static EnsurePythonObjectNode create() {
+ return EnsurePythonObjectNodeGen.create();
+ }
+
+ @NeverDefault
+ public static EnsurePythonObjectNode getUncached() {
+ return EnsurePythonObjectNodeGen.getUncached();
}
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CPyObjectArrayWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CPyObjectArrayWrapper.java
deleted file mode 100644
index e29fac9f04..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CPyObjectArrayWrapper.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (c) 2022, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.ReleaseNativeWrapperNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonStructNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.library.CachedLibrary;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
-
-import sun.misc.Unsafe;
-
-/**
- * A native wrapper for Python object arrays to be used like a {@code PyObject *arr[]}.
- */
-@ExportLibrary(InteropLibrary.class)
-public final class CPyObjectArrayWrapper extends PythonStructNativeWrapper {
-
- private static final Unsafe UNSAFE = PythonUtils.initUnsafe();
-
- private static long allocateBoundary(long size) {
- return UNSAFE.allocateMemory(size);
- }
-
- private static void freeBoundary(long ptr) {
- UNSAFE.freeMemory(ptr);
- }
-
- private final Object[] wrappers;
-
- public CPyObjectArrayWrapper(Object[] delegate) {
- super(delegate);
- wrappers = new Object[delegate.length];
- }
-
- private Object[] getObjectArray() {
- return ((Object[]) getDelegate());
- }
-
- @ExportMessage
- boolean isPointer() {
- return isNative();
- }
-
- @ExportMessage
- long asPointer() {
- return getNativePointer();
- }
-
- /**
- * Copies a Java {@code Object[]} to a native {@code PyObject *arr[]}. For this, the native
- * memory is allocated off-heap using {@code Unsafe}.
- */
- @ExportMessage
- void toNative(
- @Cached PythonToNativeNode toNativeNode,
- @CachedLibrary(limit = "3") InteropLibrary interopLib) {
- assert PythonContext.get(toNativeNode).isNativeAccessAllowed();
- if (!isNative()) {
- Object[] data = getObjectArray();
- long ptr = allocateBoundary((long) wrappers.length * Long.BYTES);
- try {
- for (int i = 0; i < data.length; i++) {
- if (wrappers[i] == null) {
- wrappers[i] = toNativeNode.execute(data[i]);
- }
- // we need a pointer, so manually send toNative
- interopLib.toNative(wrappers[i]);
- UNSAFE.putLong(ptr + (long) i * Long.BYTES, interopLib.asPointer(wrappers[i]));
- }
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- setNativePointer(ptr);
- }
- }
-
- public void free(ReleaseNativeWrapperNode releaseNativeWrapperNode) {
- /*
- * TODO we currently don't implement immediate releases of wrappers.
- *
- * If we ever do and we incref items we put in the wrappers array, we need to be careful
- * with native objects. They would need to be decref'd here and the commented out code below
- * doesn't do this.
- */
- // for (int i = 0; i < wrappers.length; i++) {
- // releaseNativeWrapperNode.execute(wrappers[i]);
- // }
- if (isNative()) {
- assert PythonContext.get(releaseNativeWrapperNode).isNativeAccessAllowed();
- freeBoundary(getNativePointer());
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionNodes.java
index 6ab36aadf8..0a309b63dd 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionNodes.java
@@ -40,105 +40,138 @@
*/
package com.oracle.graal.python.builtins.objects.cext.capi;
-import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
-import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
-import static com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper.MANAGED_REFCNT;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.CharPtrAsTruffleString;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.InitResult;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.InquiryResult;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.IterResult;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Pointer;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PrimitiveResult32;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PrimitiveResult64;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObject;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.BINARYFUNC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.BINARYFUNC_L;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.BINARYFUNC_R;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.CALL;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.DELATTRO;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.DESCR_DELETE;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.DESCR_GET;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.DESCR_SET;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.EQ;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.GE;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.GETATTR;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.GETTER;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.GT;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.HASHFUNC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.INDEXARGFUNC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.INIT;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.INQUIRYPRED;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.ITERNEXT;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.LE;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.LENFUNC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.LT;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.MP_DELITEM;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.NE;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.NEW;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.OBJOBJARGPROC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.OBJOBJPROC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.RICHCMP;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.SETATTR;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.SETATTRO;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.SETTER;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.SQ_DELITEM;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.SQ_ITEM;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.SQ_SETITEM;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.TERNARYFUNC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.TERNARYFUNC_R;
+import static com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper.UNARYFUNC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_DEALLOC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_TYPE_GENERIC_ALLOC;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectReturn;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.free;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readPtrArrayElement;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writePtrArrayElement;
import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING;
import static com.oracle.graal.python.util.PythonUtils.tsArray;
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.ref.Reference;
+import java.util.logging.Level;
+
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.ReleaseNativeWrapperNode;
+import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiGCSupport.PyObjectGCTrackNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.AsCharPointerNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.AsCharPointerNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.ReleaseNativeWrapperNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.CreateArgsTupleNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.DefaultCheckFunctionResultNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.ExternalFunctionInvokeNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.MaterializePrimitiveNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.ReleaseNativeSequenceStorageNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.GetNativeClassNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.CheckIterNextResultNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.CreateNativeArgsTupleNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.FromLongNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.FromUInt32NodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.PyObjectCheckFunctionResultNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.ReleaseNativeArgsTupleNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.ToInt32NodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.ToInt64NodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.ToPythonStringNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonReturnNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.ConvertPIntToPrimitiveNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.EnsureExecutableNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.GetIndexNode;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.ReadAndClearNativeException;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionFromNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.ConvertPIntToPrimitiveNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.cext.common.CExtToJavaNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtToNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.NativeCExtSymbol;
-import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
+import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.StorageToNativeNode;
-import com.oracle.graal.python.builtins.objects.floats.PFloat;
-import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.function.PKeyword;
import com.oracle.graal.python.builtins.objects.function.Signature;
-import com.oracle.graal.python.builtins.objects.ints.PInt;
-import com.oracle.graal.python.builtins.objects.object.PythonObject;
-import com.oracle.graal.python.builtins.objects.str.PString;
+import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
+import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
+import com.oracle.graal.python.builtins.objects.type.TypeFlags;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetTypeFlagsNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative;
+import com.oracle.graal.python.lib.PyNumberAsSizeNode;
import com.oracle.graal.python.lib.RichCmpOp;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PGuards;
-import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.PRootNode;
import com.oracle.graal.python.nodes.argument.ReadIndexedArgumentNode;
import com.oracle.graal.python.nodes.argument.ReadVarArgsNode;
import com.oracle.graal.python.nodes.argument.ReadVarKeywordsNode;
-import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
import com.oracle.graal.python.nodes.object.IsForeignObjectNode;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
import com.oracle.graal.python.runtime.ExecutionContext.CalleeContext;
-import com.oracle.graal.python.runtime.ExecutionContext.InteropCallContext;
-import com.oracle.graal.python.runtime.GilNode;
-import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.object.PFactory;
-import com.oracle.graal.python.runtime.sequence.storage.NativeObjectSequenceStorage;
import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage;
-import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage;
-import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
-import com.oracle.graal.python.util.Function;
import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateCached;
@@ -151,16 +184,11 @@
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
-import com.oracle.truffle.api.nodes.ExplodeLoop;
-import com.oracle.truffle.api.nodes.ExplodeLoop.LoopExplosionKind;
import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
+import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
import com.oracle.truffle.api.strings.TruffleString;
@@ -174,13 +202,11 @@ public abstract class ExternalFunctionNodes {
static final TruffleString[] KEYWORDS_HIDDEN_CALLABLE = new TruffleString[]{KW_CALLABLE};
static final TruffleString[] KEYWORDS_HIDDEN_CALLABLE_AND_CLOSURE = new TruffleString[]{KW_CALLABLE, KW_CLOSURE};
- public static PKeyword[] createKwDefaults(Object callable) {
- assert InteropLibrary.getUncached().isExecutable(callable);
+ public static PKeyword[] createKwDefaults(NativeFunctionPointer callable) {
return new PKeyword[]{new PKeyword(KW_CALLABLE, callable)};
}
- public static PKeyword[] createKwDefaults(Object callable, Object closure) {
- assert InteropLibrary.getUncached().isExecutable(callable);
+ public static PKeyword[] createKwDefaults(NativeFunctionPointer callable, long closure) {
return new PKeyword[]{new PKeyword(KW_CALLABLE, callable), new PKeyword(KW_CLOSURE, closure)};
}
@@ -209,11 +235,11 @@ static Object doOther(Object value) {
@NeverDefault
public static FromLongNode create() {
- return ExternalFunctionNodesFactory.FromLongNodeGen.create();
+ return FromLongNodeGen.create();
}
public static FromLongNode getUncached() {
- return ExternalFunctionNodesFactory.FromLongNodeGen.getUncached();
+ return FromLongNodeGen.getUncached();
}
}
@@ -234,11 +260,11 @@ static int doLong(long value) {
@NeverDefault
public static FromUInt32Node create() {
- return ExternalFunctionNodesFactory.FromUInt32NodeGen.create();
+ return FromUInt32NodeGen.create();
}
public static FromUInt32Node getUncached() {
- return ExternalFunctionNodesFactory.FromUInt32NodeGen.getUncached();
+ return FromUInt32NodeGen.getUncached();
}
}
@@ -263,7 +289,7 @@ static Object doOther(Object value) {
@NeverDefault
public static ToInt64Node create() {
- return ExternalFunctionNodesFactory.ToInt64NodeGen.create();
+ return ToInt64NodeGen.create();
}
}
@@ -277,19 +303,37 @@ static int doInt(int value) {
@NeverDefault
public static ToInt32Node create() {
- return ExternalFunctionNodesFactory.ToInt32NodeGen.create();
+ return ToInt32NodeGen.create();
}
}
@GenerateInline(false)
public static final class ToNativeBorrowedNode extends CExtToNativeNode {
- @Child private PythonToNativeNode toNative = PythonToNativeNodeGen.create();
+ @Child private EnsurePythonObjectNode ensurePythonObjectNode = EnsurePythonObjectNode.create();
+ @Child private PythonToNativeNode toNative = PythonToNativeNode.create();
@Override
public Object execute(Object object) {
assert (object instanceof Double && Double.isNaN((double) object)) || !(object instanceof Number || object instanceof TruffleString);
- return toNative.execute(object);
+ /*
+ * In this case, it is not necessary to explicitly keep the promoted object alive
+ * because this node is only used to hand out borrowed references which means that the
+ * returned object must be owned by a container object (e.g. a list) and we already
+ * promote the elements of such container objects at the time when the container object
+ * is handed out to native. We still need to promote the object because it could be,
+ * e.g., Java primitive 'true' which will be promoted to an immortal object.
+ */
+ PythonContext ctx = PythonContext.get(this);
+ Object promoted = ensurePythonObjectNode.execute(ctx, object, false);
+ assert promoted == object || PythonToNativeInternalNode.isImmortal(ctx, promoted);
+ return toNative.executeLong(promoted);
+ }
+
+ @TruffleBoundary(allowInlining = true)
+ public static long executeUncached(Object object) {
+ Object promoted = EnsurePythonObjectNode.executeUncached(PythonContext.get(null), object, false);
+ return PythonToNativeNode.executeLongUncached(promoted);
}
}
@@ -297,11 +341,11 @@ public Object execute(Object object) {
@GenerateInline(false)
public abstract static class ToPythonStringNode extends CExtToJavaNode {
@Specialization
- static Object doIt(Object object,
+ static Object doIt(long pointer,
@Bind Node inliningTarget,
@Cached CastToTruffleStringNode castToStringNode,
@Cached NativeToPythonNode nativeToPythonNode) {
- Object result = nativeToPythonNode.execute(object);
+ Object result = nativeToPythonNode.executeRaw(pointer);
if (result == PNone.NO_VALUE) {
return result;
}
@@ -310,616 +354,281 @@ static Object doIt(Object object,
@NeverDefault
public static ToPythonStringNode create() {
- return ExternalFunctionNodesFactory.ToPythonStringNodeGen.create();
+ return ToPythonStringNodeGen.create();
}
public static ToPythonStringNode getUncached() {
- return ExternalFunctionNodesFactory.ToPythonStringNodeGen.getUncached();
+ return ToPythonStringNodeGen.getUncached();
}
}
- /**
- * Enum of well-known function and slot signatures. The integer values must stay in sync with
- * the definition in {code capi.h}.
- */
+ /** Enum of all slot wrapper functions in {@code typeobject.c}. */
public enum PExternalFunctionWrapper implements NativeCExtSymbol {
- DIRECT(1, PyObjectTransfer, PyObject, PyObject), // TODO: remove?
- FASTCALL(2, PyObjectTransfer, PyObject, Pointer, Py_ssize_t),
- FASTCALL_WITH_KEYWORDS(3, PyObjectTransfer, PyObject, Pointer, Py_ssize_t, PyObject),
- KEYWORDS(4, PyObjectTransfer, PyObject, PyObject, PyObject), // METH_VARARGS | METH_KEYWORDS
- VARARGS(5, PyObjectTransfer, PyObject, PyObject), // METH_VARARGS
- NOARGS(6, PyObjectTransfer, PyObject, PyObject), // METH_NOARGS
- O(7, PyObjectTransfer, PyObject, PyObject), // METH_O
- // METH_FASTCALL | METH_KEYWORDS | METH_METHOD:
- METHOD(8, PyObjectTransfer, PyObject, PyTypeObject, Pointer, Py_ssize_t, PyObject),
- ALLOC(10, PyObjectTransfer, PyTypeObject, Py_ssize_t),
- GETATTR(11, PyObjectTransfer, PyObject, CharPtrAsTruffleString),
- SETATTR(12, InitResult, PyObject, CharPtrAsTruffleString, PyObject),
- RICHCMP(13, PyObjectTransfer, PyObject, PyObject, Int),
- SETITEM(14, InitResult, PyObject, Py_ssize_t, PyObject),
- UNARYFUNC(15, PyObjectTransfer, PyObject),
- BINARYFUNC(16, PyObjectTransfer, PyObject, PyObject),
- BINARYFUNC_L(17, PyObjectTransfer, PyObject, PyObject),
- BINARYFUNC_R(18, PyObjectTransfer, PyObject, PyObject),
- TERNARYFUNC(19, PyObjectTransfer, PyObject, PyObject, PyObject),
- TERNARYFUNC_R(20, PyObjectTransfer, PyObject, PyObject, PyObject),
- LT(21, PyObjectTransfer, PyObject, PyObject, Int),
- LE(22, PyObjectTransfer, PyObject, PyObject, Int),
- EQ(23, PyObjectTransfer, PyObject, PyObject, Int),
- NE(24, PyObjectTransfer, PyObject, PyObject, Int),
- GT(25, PyObjectTransfer, PyObject, PyObject, Int),
- GE(26, PyObjectTransfer, PyObject, PyObject, Int),
- ITERNEXT(27, IterResult, PyObject),
- INQUIRY(28, InquiryResult, PyObject),
- DELITEM(29, defaults(1), Int, PyObject, Py_ssize_t, PyObject),
- GETITEM(30, PyObjectTransfer, PyObject, Py_ssize_t),
- GETTER(31, PyObjectTransfer, PyObject, Pointer),
- SETTER(32, InitResult, PyObject, PyObject, Pointer),
- INITPROC(33, InitResult, PyObject, PyObject, PyObject),
- HASHFUNC(34, PrimitiveResult64, PyObject),
- CALL(35, PyObjectTransfer, PyObject, PyObject, PyObject),
- SETATTRO(36, InitResult, PyObject, PyObject, PyObject),
- DESCR_GET(37, defaults(1), PyObjectTransfer, PyObject, PyObject, PyObject),
- DESCR_SET(38, InitResult, PyObject, PyObject, PyObject),
- LENFUNC(39, PrimitiveResult64, PyObject),
- OBJOBJPROC(40, InquiryResult, PyObject, PyObject),
- OBJOBJARGPROC(41, PrimitiveResult32, PyObject, PyObject, PyObject),
- NEW(42, PyObjectTransfer, PyObject, PyObject, PyObject),
- MP_DELITEM(43, PrimitiveResult32, PyObject, PyObject, PyObject),
- TP_STR(44, PyObjectTransfer, PyObject),
- TP_REPR(45, PyObjectTransfer, PyObject),
- DESCR_DELETE(46, InitResult, PyObject, PyObject, PyObject), // the last one is always NULL
- DELATTRO(47, InitResult, PyObject, PyObject, PyObject), // the last one is always NULL
- SSIZE_ARG(48, PyObjectTransfer, PyObject, Py_ssize_t),
- VISITPROC(49, Int, PyObject, Pointer),
- TRAVERSEPROC(50, Int, PyObject, Pointer, Pointer);
-
- private static int defaults(int x) {
- return x;
- }
-
- @CompilationFinal(dimensions = 1) private static final PExternalFunctionWrapper[] VALUES = values();
- @CompilationFinal(dimensions = 1) private static final PExternalFunctionWrapper[] BY_ID = new PExternalFunctionWrapper[51];
-
- public final String signature;
- public final ArgDescriptor returnValue;
- public final ArgDescriptor[] arguments;
- public final int numDefaults;
+ GETATTR(GetAttrFuncRootNode.class, ExternalFunctionSignature.GETATTRFUNC),
+ SETATTR(SetAttrFuncRootNode.class, ExternalFunctionSignature.SETATTRFUNC),
- PExternalFunctionWrapper(int value, int numDefaults, ArgDescriptor returnValue, ArgDescriptor... arguments) {
- this.value = value;
- this.returnValue = returnValue;
- this.arguments = arguments;
+ RICHCMP(RichCmpFuncRootNode.class, ExternalFunctionSignature.RICHCMPFUNC),
- StringBuilder s = new StringBuilder("(");
- for (int i = 0; i < arguments.length; i++) {
- s.append(i == 0 ? "" : ",");
- s.append(arguments[i].getNFISignature());
- }
- s.append("):").append(returnValue.getNFISignature());
- this.signature = s.toString();
- this.numDefaults = numDefaults;
- }
+ // wrap_sq_setitem
+ SQ_SETITEM(SetItemRootNode.class, ExternalFunctionSignature.SSIZEOBJARGPROC),
- PExternalFunctionWrapper(int value, ArgDescriptor returnValue, ArgDescriptor... arguments) {
- this(value, 0, returnValue, arguments);
- }
+ // wrap_unaryfunc
+ UNARYFUNC(MethUnaryFunc.class, ExternalFunctionSignature.UNARYFUNC),
- private final int value;
+ // wrap_binaryfunc
+ BINARYFUNC(MethBinaryRoot.class, ExternalFunctionSignature.BINARYFUNC),
- static {
- for (var e : VALUES) {
- assert BY_ID[e.value] == null;
- BY_ID[e.value] = e;
- }
- }
+ // wrap_binaryfunc_l
+ BINARYFUNC_L(MethBinaryRoot.class, ExternalFunctionSignature.BINARYFUNC),
- static PExternalFunctionWrapper fromValue(int value) {
- return value >= 0 && value < BY_ID.length ? BY_ID[value] : null;
- }
+ // wrap_binaryfunc_r
+ BINARYFUNC_R(MethBinaryRoot.class, ExternalFunctionSignature.BINARYFUNC),
- static PExternalFunctionWrapper fromMethodFlags(int flags) {
- if (CExtContext.isMethNoArgs(flags)) {
- return NOARGS;
- } else if (CExtContext.isMethO(flags)) {
- return O;
- } else if (CExtContext.isMethVarargsWithKeywords(flags)) {
- return KEYWORDS;
- } else if (CExtContext.isMethVarargs(flags)) {
- return VARARGS;
- } else if (CExtContext.isMethMethod(flags)) {
- return METHOD;
- } else if (CExtContext.isMethFastcallWithKeywords(flags)) {
- return FASTCALL_WITH_KEYWORDS;
- } else if (CExtContext.isMethFastcall(flags)) {
- return FASTCALL;
- }
- throw CompilerDirectives.shouldNotReachHere("illegal method flags");
+ // wrap_ternaryfunc
+ TERNARYFUNC(MethTernaryFuncRoot.class, ExternalFunctionSignature.TERNARYFUNC, 1),
+
+ // wrap_ternaryfunc_r
+ TERNARYFUNC_R(MethTernaryFuncRoot.class, ExternalFunctionSignature.TERNARYFUNC, 1),
+
+ // richcmp_lt
+ LT(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC),
+
+ // richcmp_le
+ LE(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC),
+
+ // richcmp_eq
+ EQ(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC),
+
+ // richcmp_ne
+ NE(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC),
+
+ // richcmp_gt
+ GT(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC),
+
+ // richcmp_ge
+ GE(MethRichcmpOpRootNode.class, ExternalFunctionSignature.RICHCMPFUNC),
+
+ // wrap_next
+ ITERNEXT(IterNextFuncRootNode.class, ExternalFunctionSignature.UNARYFUNC),
+
+ // wrap_inquirypred
+ INQUIRYPRED(MethInquiryRoot.class, ExternalFunctionSignature.INQUIRY),
+
+ // wrap_sq_delitem
+ SQ_DELITEM(SqDelItemRootNode.class, ExternalFunctionSignature.SSIZEOBJARGPROC),
+
+ // wrap_sq_item
+ SQ_ITEM(GetItemRootNode.class, ExternalFunctionSignature.SSIZEARGFUNC),
+
+ // wrap_init
+ INIT(MethInitRoot.class, ExternalFunctionSignature.INITPROC),
+
+ // wrap_hashfunc
+ HASHFUNC(MethLenfuncRoot.class, ExternalFunctionSignature.HASHFUNC),
+
+ // wrap_call
+ CALL(MethInitRoot.class, ExternalFunctionSignature.TERNARYFUNC),
+
+ // wrap_setattr
+ SETATTRO(SetAttrOFuncRootNode.class, ExternalFunctionSignature.SETATTROFUNC),
+
+ DESCR_GET(DescrGetRootNode.class, ExternalFunctionSignature.DESCRGETFUNC, 1),
+
+ // wrap_descr_set
+ DESCR_SET(MethDescrSetRoot.class, ExternalFunctionSignature.DESCRSETFUNC),
+
+ // wrap_lenfunc
+ LENFUNC(MethLenfuncRoot.class, ExternalFunctionSignature.LENFUNC),
+
+ // wrap_objobjproc
+ OBJOBJPROC(MethObjObjProcRoot.class, ExternalFunctionSignature.OBJOBJPROC),
+
+ // wrap_objobjargproc
+ OBJOBJARGPROC(MethObjObjArgProcRoot.class, ExternalFunctionSignature.OBJOBJARGPROC),
+
+ // tp_new_wrapper
+ NEW(MethNewRoot.class, ExternalFunctionSignature.NEWFUNC),
+
+ // wrap_delitem
+ MP_DELITEM(MpDelItemRootNode.class, ExternalFunctionSignature.OBJOBJARGPROC),
+
+ // wrap_descr_delete
+ DESCR_DELETE(DescrDeleteRootNode.class, ExternalFunctionSignature.DESCRSETFUNC),
+
+ // wrap_delattr
+ DELATTRO(DelAttrRootNode.class, ExternalFunctionSignature.SETATTROFUNC),
+
+ INDEXARGFUNC(IndexArgFuncRootNode.class, ExternalFunctionSignature.SSIZEARGFUNC),
+
+ GETTER(GetterRoot.class, ExternalFunctionSignature.GETTER),
+ SETTER(SetterRoot.class, ExternalFunctionSignature.SETTER),
+
+ // TRAVERSEPROC(null, Int, PyObject, Pointer, Pointer);
+ TRAVERSEPROC(null, null);
+
+ final Class extends WrapperDescriptorRoot> rootNodeClass;
+
+ public final ExternalFunctionSignature signature;
+ public final int numDefaults;
+
+ PExternalFunctionWrapper(Class extends WrapperDescriptorRoot> rootNodeClass, ExternalFunctionSignature signature) {
+ this(rootNodeClass, signature, 0);
}
- @TruffleBoundary
- static RootCallTarget getOrCreateCallTarget(PExternalFunctionWrapper sig, PythonLanguage language, TruffleString name, boolean isStatic) {
- Class extends PRootNode> nodeKlass;
- Function rootNodeFunction;
- switch (sig) {
- case ALLOC:
- case SSIZE_ARG:
- nodeKlass = AllocFuncRootNode.class;
- rootNodeFunction = (l -> new AllocFuncRootNode(l, name, sig));
- break;
- case DIRECT:
- case DESCR_SET:
- case LENFUNC:
- case HASHFUNC:
- case SETATTRO:
- case OBJOBJPROC:
- case OBJOBJARGPROC:
- case UNARYFUNC:
- case BINARYFUNC:
- case BINARYFUNC_L:
- case TP_STR:
- case TP_REPR:
- nodeKlass = MethDirectRoot.class;
- rootNodeFunction = l -> MethDirectRoot.create(language, name, sig);
- break;
- case CALL:
- case INITPROC:
- case KEYWORDS:
- nodeKlass = MethKeywordsRoot.class;
- rootNodeFunction = l -> new MethKeywordsRoot(l, name, isStatic, sig);
- break;
- case NEW:
- nodeKlass = MethNewRoot.class;
- rootNodeFunction = l -> new MethNewRoot(l, name, isStatic, sig);
- break;
- case VARARGS:
- nodeKlass = MethVarargsRoot.class;
- rootNodeFunction = (l -> new MethVarargsRoot(l, name, isStatic, sig));
- break;
- case INQUIRY:
- nodeKlass = MethInquiryRoot.class;
- rootNodeFunction = (l -> new MethInquiryRoot(l, name, isStatic, sig));
- break;
- case NOARGS:
- nodeKlass = MethNoargsRoot.class;
- rootNodeFunction = (l -> new MethNoargsRoot(l, name, isStatic, sig));
- break;
- case O:
- nodeKlass = MethORoot.class;
- rootNodeFunction = (l -> new MethORoot(l, name, isStatic, sig));
- break;
- case FASTCALL:
- nodeKlass = MethFastcallRoot.class;
- rootNodeFunction = (l -> new MethFastcallRoot(l, name, isStatic, sig));
- break;
- case FASTCALL_WITH_KEYWORDS:
- nodeKlass = MethFastcallWithKeywordsRoot.class;
- rootNodeFunction = (l -> new MethFastcallWithKeywordsRoot(l, name, isStatic, sig));
- break;
- case METHOD:
- nodeKlass = MethMethodRoot.class;
- rootNodeFunction = (l -> new MethMethodRoot(l, name, isStatic, sig));
- break;
- case GETATTR:
- nodeKlass = GetAttrFuncRootNode.class;
- rootNodeFunction = (l -> new GetAttrFuncRootNode(l, name, sig));
- break;
- case SETATTR:
- nodeKlass = SetAttrFuncRootNode.class;
- rootNodeFunction = (l -> new SetAttrFuncRootNode(l, name, sig));
- break;
- case DESCR_GET:
- nodeKlass = DescrGetRootNode.class;
- rootNodeFunction = (l -> new DescrGetRootNode(l, name, sig));
- break;
- case DESCR_DELETE:
- nodeKlass = DescrGetRootNode.class;
- rootNodeFunction = (l -> new DescrDeleteRootNode(l, name, sig));
- break;
- case DELATTRO:
- nodeKlass = DelAttrRootNode.class;
- rootNodeFunction = (l -> new DelAttrRootNode(l, name, sig));
- break;
- case RICHCMP:
- nodeKlass = RichCmpFuncRootNode.class;
- rootNodeFunction = (l -> new RichCmpFuncRootNode(l, name, sig));
- break;
- case SETITEM:
- case DELITEM:
- nodeKlass = SetItemRootNode.class;
- rootNodeFunction = (l -> new SetItemRootNode(l, name, sig));
- break;
- case GETITEM:
- nodeKlass = GetItemRootNode.class;
- rootNodeFunction = (l -> new GetItemRootNode(l, name, sig));
- break;
- case BINARYFUNC_R:
- nodeKlass = MethReverseRootNode.class;
- rootNodeFunction = (l -> new MethReverseRootNode(l, name, sig));
- break;
- case TERNARYFUNC:
- nodeKlass = MethPowRootNode.class;
- rootNodeFunction = (l -> new MethPowRootNode(l, name, sig));
- break;
- case TERNARYFUNC_R:
- nodeKlass = MethRPowRootNode.class;
- rootNodeFunction = (l -> new MethRPowRootNode(l, name, sig));
- break;
- case GT:
- case GE:
- case LE:
- case LT:
- case EQ:
- case NE:
- nodeKlass = MethRichcmpOpRootNode.class;
- int op = getCompareOpCode(sig);
- rootNodeFunction = (l -> new MethRichcmpOpRootNode(l, name, sig, op));
- break;
- case ITERNEXT:
- nodeKlass = IterNextFuncRootNode.class;
- rootNodeFunction = (l -> new IterNextFuncRootNode(l, name, sig));
- break;
- case GETTER:
- nodeKlass = GetterRoot.class;
- rootNodeFunction = l -> new GetterRoot(l, name, sig);
- break;
- case SETTER:
- nodeKlass = SetterRoot.class;
- rootNodeFunction = l -> new SetterRoot(l, name, sig);
- break;
- case MP_DELITEM:
- nodeKlass = MpDelItemRootNode.class;
- rootNodeFunction = (l -> new MpDelItemRootNode(l, name, sig));
- break;
- default:
- throw CompilerDirectives.shouldNotReachHere();
- }
- return language.createCachedExternalFunWrapperCallTarget(rootNodeFunction, nodeKlass, sig, name, true, isStatic);
+ PExternalFunctionWrapper(Class extends WrapperDescriptorRoot> rootNodeClass, ExternalFunctionSignature signature, int numDefaults) {
+ this.rootNodeClass = rootNodeClass;
+ this.signature = signature;
+ this.numDefaults = numDefaults;
}
- public static PythonObject createWrapperFunction(TruffleString name, Object callable, Object enclosingType, int flags, int sig,
- PythonLanguage language) {
- return createWrapperFunction(name, callable, enclosingType, flags, PExternalFunctionWrapper.fromValue(sig), language);
+ @TruffleBoundary
+ static RootCallTarget getOrCreateCallTarget(PExternalFunctionWrapper sig, PythonLanguage language, TruffleString name) {
+ return language.createCachedExternalFunWrapperCallTarget(l -> WrapperDescriptorRootNodesGen.create(l, name, sig), sig.rootNodeClass, sig, name, true, false);
}
/**
- * Creates a built-in function for a specific signature. This built-in function also does
- * appropriate argument and result conversion and calls the provided callable.
+ * Similar to Python API function {@code PyDescr_NewWrapper}, creates a built-in function of
+ * type {@link PythonBuiltinClassType#WrapperDescriptor} (usually for a slot). This built-in
+ * function also does appropriate argument and result conversion and calls the provided
+ * native function.
*
- * @param language The Python language object.
- * @param sig The wrapper/signature ID as defined in {@link PExternalFunctionWrapper}.
* @param name The name of the method.
* @param callable A reference denoting executable code. Currently, there are two
* representations for that: a native function pointer or a
* {@link RootCallTarget}
* @param enclosingType The type the function belongs to (needed for checking of
* {@code self}).
+ * @param sig The wrapper/signature ID as defined in {@link PExternalFunctionWrapper}.
+ * @param language The Python language object.
* @return A {@link PBuiltinFunction} implementing the semantics of the specified slot
* wrapper.
*/
@TruffleBoundary
- public static PythonObject createWrapperFunction(TruffleString name, Object callable, Object enclosingType, int flags, PExternalFunctionWrapper sig, PythonLanguage language) {
- LOGGER.finer(() -> PythonUtils.formatJString("ExternalFunctions.createWrapperFunction(%s, %s)", name, callable));
- if (flags < 0) {
- flags = 0;
- }
- RootCallTarget callTarget = getOrCreateCallTarget(sig, language, name, CExtContext.isMethStatic(flags));
+ public static PythonBuiltinObject createDescrWrapperFunction(TruffleString name, NativeFunctionPointer callable, Object enclosingType, PExternalFunctionWrapper sig, PythonLanguage language) {
+ LOGGER.finer(() -> PythonUtils.formatJString("ExternalFunctions.createDescrWrapperFunction(%s, %s)", name, callable));
+ RootCallTarget callTarget = getOrCreateCallTarget(sig, language, name);
// ensure that 'callable' is executable via InteropLibrary
- Object boundCallable = EnsureExecutableNode.executeUncached(callable, sig);
- PKeyword[] kwDefaults = ExternalFunctionNodes.createKwDefaults(boundCallable);
- TpSlot slot = TpSlotNative.createCExtSlot(boundCallable);
+ PKeyword[] kwDefaults = ExternalFunctionNodes.createKwDefaults(callable);
+ TpSlot slot = TpSlotNative.createCExtSlot(callable);
// generate default values for positional args (if necessary)
Object[] defaults = PBuiltinFunction.generateDefaults(sig.numDefaults);
Object type = enclosingType == PNone.NO_VALUE ? null : enclosingType;
- return switch (sig) {
- case NOARGS, O, VARARGS, KEYWORDS, FASTCALL, FASTCALL_WITH_KEYWORDS, METHOD ->
- PFactory.createBuiltinFunction(language, name, type, defaults, kwDefaults, flags, callTarget);
- case NEW -> PFactory.createNewWrapper(language, type, defaults, kwDefaults, callTarget, slot);
- default -> PFactory.createWrapperDescriptor(language, name, type, defaults, kwDefaults, flags, callTarget, slot, sig);
- };
- }
-
- private static int getCompareOpCode(PExternalFunctionWrapper sig) {
- // op codes for binary comparisons (defined in 'object.h')
- return switch (sig) {
- case LT -> RichCmpOp.Py_LT.asNative();
- case LE -> RichCmpOp.Py_LE.asNative();
- case EQ -> RichCmpOp.Py_EQ.asNative();
- case NE -> RichCmpOp.Py_NE.asNative();
- case GT -> RichCmpOp.Py_GT.asNative();
- case GE -> RichCmpOp.Py_GE.asNative();
- default -> throw CompilerDirectives.shouldNotReachHere(sig.getName());
- };
- }
-
- CheckFunctionResultNode createCheckFunctionResultNode() {
- return returnValue.createCheckResultNode();
- }
-
- CheckFunctionResultNode getUncachedCheckFunctionResultNode() {
- return returnValue.getUncachedCheckResultNode();
- }
-
- CExtToJavaNode createConvertRetNode() {
- return returnValue.createNativeToPythonNode();
- }
-
- CExtToJavaNode getUncachedConvertRetNode() {
- return returnValue.getUncachedNativeToPythonNode();
- }
-
- CExtToNativeNode[] createConvertArgNodes() {
- return createConvertArgNodes(arguments);
- }
-
- public static CExtToNativeNode[] createConvertArgNodes(ArgDescriptor[] descriptors) {
- CExtToNativeNode[] result = new CExtToNativeNode[descriptors.length];
- for (int i = 0; i < descriptors.length; i++) {
- result[i] = descriptors[i].createPythonToNativeNode();
+ if (sig == NEW) {
+ return PFactory.createNewWrapper(language, type, defaults, kwDefaults, callTarget, slot);
}
- return result;
+ return PFactory.createWrapperDescriptor(language, name, type, defaults, kwDefaults, 0, callTarget, slot, sig);
}
+ @Override
public String getName() {
return name();
}
+ @Override
public TruffleString getTsName() {
throw CompilerDirectives.shouldNotReachHere();
}
- public String getSignature() {
- return signature;
- }
- }
-
- private static Signature createSignature(boolean takesVarKeywordArgs, int varArgIndex, TruffleString[] parameters, boolean checkEnclosingType, boolean hidden) {
- return new Signature(-1, takesVarKeywordArgs, varArgIndex, parameters, KEYWORDS_HIDDEN_CALLABLE, checkEnclosingType, T_EMPTY_STRING, hidden);
- }
-
- private static Signature createSignatureWithClosure(boolean takesVarKeywordArgs, int varArgIndex, TruffleString[] parameters, boolean checkEnclosingType, boolean hidden) {
- return new Signature(-1, takesVarKeywordArgs, varArgIndex, parameters, KEYWORDS_HIDDEN_CALLABLE_AND_CLOSURE, checkEnclosingType, T_EMPTY_STRING, hidden);
- }
-
- static final class MethDirectRoot extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(true, 0, null, false, true);
-
- private MethDirectRoot(PythonLanguage lang, TruffleString name, PExternalFunctionWrapper provider) {
- super(lang, name, true, provider);
- }
-
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- // return a copy of the args array since it will be modified
- Object[] varargs = (Object[]) PArguments.getArgument(frame, SIGNATURE.varArgsPArgumentsIndex());
- return PythonUtils.arrayCopyOf(varargs, varargs.length);
- }
-
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- for (int i = 0; i < cArguments.length; i++) {
- ensureReleaseNativeWrapperNode().execute(cArguments[i]);
- }
+ public ArgDescriptor getReturnValue() {
+ return signature.returnValue;
}
@Override
- public Signature getSignature() {
- return SIGNATURE;
- }
-
- @TruffleBoundary
- public static MethDirectRoot create(PythonLanguage lang, TruffleString name, PExternalFunctionWrapper provider) {
- return new MethDirectRoot(lang, name, provider);
+ public ArgDescriptor[] getArguments() {
+ return signature.arguments;
}
}
- @GenerateUncached
- @GenerateCached(false)
- @GenerateInline
- public abstract static class ExternalFunctionInvokeNode extends PNodeWithContext {
- abstract Object execute(VirtualFrame frame, Node inliningTarget, PythonThreadState threadState, CApiTiming timing, TruffleString name, Object callable, Object[] cArguments);
-
- public final Object call(VirtualFrame frame, Node inliningTarget, PythonThreadState threadState, CApiTiming timing, TruffleString name, Object callable, Object... cArguments) {
- return execute(frame, inliningTarget, threadState, timing, name, callable, cArguments);
- }
-
- @Specialization
- static Object invoke(VirtualFrame frame, Node inliningTarget, PythonThreadState threadState, CApiTiming timing, TruffleString name, Object callable, Object[] cArguments,
- @Cached("createFor($node)") InteropCallData boundaryCallData,
- @CachedLibrary(limit = "2") InteropLibrary lib) {
-
- // If any code requested the caught exception (i.e. used 'sys.exc_info()'), we store
- // it to the context since we cannot propagate it through the native frames.
- Object state = InteropCallContext.enter(frame, threadState, boundaryCallData);
-
- CApiTiming.enter();
- try {
- return lib.execute(callable, cArguments);
- } catch (UnsupportedTypeException | UnsupportedMessageException e) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CALLING_NATIVE_FUNC_FAILED, name, e);
- } catch (ArityException e) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CALLING_NATIVE_FUNC_EXPECTED_ARGS, name, e.getExpectedMinArity(), e.getActualArity());
- } catch (Throwable exception) {
- /*
- * Always re-acquire the GIL here. This is necessary because it could happen that C
- * extensions are releasing the GIL and if then an LLVM exception occurs, C code
- * wouldn't re-acquire it (unexpectedly).
- */
- CompilerDirectives.transferToInterpreterAndInvalidate();
- GilNode.uncachedAcquire();
- throw exception;
- } finally {
- CApiTiming.exit(timing);
- /*
- * Special case after calling a C function: transfer caught exception back to frame
- * to simulate the global state semantics.
- */
- if (frame != null && threadState.getCaughtException() != null) {
- PArguments.setException(frame, threadState.getCaughtException());
- }
- InteropCallContext.exit(frame, threadState, state);
- }
- }
+ /**
+ * A marker annotation used to denote root nodes that perform external function invocation. The
+ * annotated elements need to be extendable and are expected to have an abstract method
+ * {@code protected abstract invokeExternalFunction(VirtualFrame frame, PythonContext context, NativeFunctionPointer nativeFunction, , , ..., )}
+ * where the {@code returnType} matches the {@link ExternalFunctionSignature#returnValue} Java
+ * type and same for the arguments {@link ExternalFunctionSignature#arguments}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @Target(ElementType.TYPE)
+ public @interface CApiWrapperDescriptor {
+ PExternalFunctionWrapper[] value();
}
/**
- * Wraps {@link ExternalFunctionInvokeNode} with result checking and conversion according to the
- * passed {@link PExternalFunctionWrapper}. This node assumes that the provider argument is in
- * the cached case a PE constant.
+ * A marker annotation used to denote root nodes that perform external function invocation. The
+ * annotated elements need to be extendable and are expected to have an abstract method
+ * {@code protected abstract invokeExternalFunction(VirtualFrame frame, PythonContext context, NativeFunctionPointer nativeFunction, , , ..., )}
+ * where the {@code returnType} matches the {@link ExternalFunctionSignature#returnValue} Java
+ * type and same for the arguments {@link ExternalFunctionSignature#arguments}.
*/
- @GenerateInline(false)
- public abstract static class ExternalFunctionWrapperInvokeNode extends PNodeWithContext {
- public abstract Object execute(VirtualFrame frame, PExternalFunctionWrapper provider, CApiTiming timing, TruffleString name, Object callable, Object[] cArguments);
-
- @NeverDefault
- static CheckFunctionResultNode createCheckResultNode(PExternalFunctionWrapper provider) {
- CheckFunctionResultNode node = provider.createCheckFunctionResultNode();
- return node != null ? node : DefaultCheckFunctionResultNodeGen.create();
- }
+ @Retention(RetentionPolicy.SOURCE)
+ @Target(ElementType.METHOD)
+ public @interface InvokeExternalFunction {
+ ExternalFunctionSignature value();
- static CheckFunctionResultNode getUncachedCheckResultNode(PExternalFunctionWrapper provider) {
- CheckFunctionResultNode node = provider.getUncachedCheckFunctionResultNode();
- return node != null ? node : DefaultCheckFunctionResultNodeGen.getUncached();
- }
+ Class> retConversion() default long.class;
- @Specialization
- static Object invokeCached(VirtualFrame frame, PExternalFunctionWrapper provider, CApiTiming timing, TruffleString name, Object callable, Object[] cArguments,
- @Bind Node inliningTarget,
- @Cached("createCheckResultNode(provider)") CheckFunctionResultNode checkResultNode,
- @SuppressWarnings("truffle-neverdefault") @Cached("provider.createConvertRetNode()") CExtToJavaNode convertReturnValue,
- @Cached PForeignToPTypeNode fromForeign,
- @Cached GetThreadStateNode getThreadStateNode,
- @Cached ExternalFunctionInvokeNode invokeNode) {
- CompilerAsserts.partialEvaluationConstant(provider);
- PythonContext ctx = PythonContext.get(inliningTarget);
- return invoke(frame, ctx, timing, name, callable, cArguments, inliningTarget, checkResultNode, convertReturnValue, fromForeign, getThreadStateNode, invokeNode);
- }
-
- private static Object invoke(VirtualFrame frame, PythonContext ctx, CApiTiming timing, TruffleString name, Object callable, Object[] cArguments, Node inliningTarget,
- CheckFunctionResultNode checkResultNode, CExtToJavaNode convertReturnValue, PForeignToPTypeNode fromForeign, GetThreadStateNode getThreadStateNode,
- ExternalFunctionInvokeNode invokeNode) {
- PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = invokeNode.execute(frame, inliningTarget, threadState, timing, name, callable, cArguments);
- result = checkResultNode.execute(threadState, name, result);
- if (convertReturnValue != null) {
- result = convertReturnValue.execute(result);
- }
- return fromForeign.executeConvert(result);
- }
-
- @GenerateCached(false)
- private static final class ExternalFunctionWrapperInvokeNodeUncached extends ExternalFunctionWrapperInvokeNode {
- private static final ExternalFunctionWrapperInvokeNodeUncached INSTANCE = new ExternalFunctionWrapperInvokeNodeUncached();
-
- @Override
- public Object execute(VirtualFrame frame, PExternalFunctionWrapper provider, CApiTiming timing, TruffleString name, Object callable, Object[] cArguments) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- PythonContext ctx = PythonContext.get(null);
- return invoke(frame, ctx, timing, name, callable, cArguments, null, getUncachedCheckResultNode(provider), provider.getUncachedConvertRetNode(), PForeignToPTypeNode.getUncached(),
- GetThreadStateNode.getUncached(), ExternalFunctionInvokeNodeGen.getUncached());
- }
- }
+ Class>[] argConversions();
+ }
- @NeverDefault
- public static ExternalFunctionWrapperInvokeNode create() {
- return ExternalFunctionNodesFactory.ExternalFunctionWrapperInvokeNodeGen.create();
- }
+ private static Signature createSignature(boolean takesVarKeywordArgs, int varArgIndex, TruffleString[] parameters, boolean checkEnclosingType, boolean hidden) {
+ return new Signature(-1, takesVarKeywordArgs, varArgIndex, parameters, KEYWORDS_HIDDEN_CALLABLE, checkEnclosingType, T_EMPTY_STRING, hidden);
+ }
- public static ExternalFunctionWrapperInvokeNode getUncached() {
- return ExternalFunctionWrapperInvokeNodeUncached.INSTANCE;
- }
+ private static Signature createSignatureWithClosure(boolean takesVarKeywordArgs, int varArgIndex, TruffleString[] parameters, boolean checkEnclosingType, boolean hidden) {
+ return new Signature(-1, takesVarKeywordArgs, varArgIndex, parameters, KEYWORDS_HIDDEN_CALLABLE_AND_CLOSURE, checkEnclosingType, T_EMPTY_STRING, hidden);
}
- public abstract static class MethodDescriptorRoot extends PRootNode {
- private final PExternalFunctionWrapper provider;
- private final CApiTiming timing;
- @Child private CalleeContext calleeContext = CalleeContext.create();
- @Child private ExternalFunctionWrapperInvokeNode externalInvokeNode;
+ public abstract static class WrapperBaseRoot extends PRootNode {
+ @Child private CalleeContext calleeContext;
@Child private ReadIndexedArgumentNode readSelfNode;
@Child private ReadIndexedArgumentNode readCallableNode;
- @Child private ReleaseNativeWrapperNode releaseNativeWrapperNode;
- @Children private final CExtToNativeNode[] convertArgs;
+ @Child private EnsurePythonObjectNode ensurePythonObjectNode;
- private final TruffleString name;
+ protected final TruffleString name;
- MethodDescriptorRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
+ WrapperBaseRoot(PythonLanguage language, TruffleString name, boolean isStatic) {
super(language);
CompilerAsserts.neverPartOfCompilation();
this.name = name;
- this.timing = CApiTiming.create(true, name);
- this.provider = provider;
- this.externalInvokeNode = ExternalFunctionWrapperInvokeNode.create();
- this.convertArgs = provider.createConvertArgNodes();
if (!isStatic) {
readSelfNode = ReadIndexedArgumentNode.create(0);
}
}
- @ExplodeLoop
- private void prepareArguments(Object[] arguments) {
- for (int i = 0; i < convertArgs.length; i++) {
- if (convertArgs[i] != null) {
- arguments[i] = convertArgs[i].execute(arguments[i]);
- }
- }
- }
+ protected abstract Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction);
@Override
public final Object execute(VirtualFrame frame) {
+ if (calleeContext == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ assert readCallableNode == null;
+ assert calleeContext == null;
+ // we insert a hidden argument at the end of the positional arguments
+ int hiddenArg = getSignature().getParameterIds().length;
+ readCallableNode = insert(ReadIndexedArgumentNode.create(hiddenArg));
+ calleeContext = insert(CalleeContext.create());
+ }
calleeContext.enter(frame, this);
try {
- Object callable = ensureReadCallableNode().execute(frame);
- Object[] cArguments = prepareCArguments(frame);
- prepareArguments(cArguments);
- try {
- assert this.provider != null : "the provider cannot be null";
- return externalInvokeNode.execute(frame, provider, timing, name, callable, cArguments);
- } finally {
- postprocessCArguments(frame, cArguments);
+ Object callable = readCallableNode.execute(frame);
+ if (!(callable instanceof NativeFunctionPointer boundFunction)) {
+ throw CompilerDirectives.shouldNotReachHere();
}
+ return readArgumentsAndInvokeExternalFunction(frame, boundFunction);
} finally {
calleeContext.exit(frame, this);
}
}
- /**
- * Prepare the arguments for calling the C function. The arguments will then be converted to
- * LLVM arguments using the {@link ArgDescriptor#createPythonToNativeNode()}. This will
- * modify the returned array.
- */
- protected abstract Object[] prepareCArguments(VirtualFrame frame);
-
- @SuppressWarnings("unused")
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- // default: do nothing
- }
-
- private ReadIndexedArgumentNode ensureReadCallableNode() {
- if (readCallableNode == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- // we insert a hidden argument at the end of the positional arguments
- int hiddenArg = getSignature().getParameterIds().length;
- readCallableNode = insert(ReadIndexedArgumentNode.create(hiddenArg));
- }
- return readCallableNode;
- }
-
- protected final ReleaseNativeWrapperNode ensureReleaseNativeWrapperNode() {
- if (releaseNativeWrapperNode == null) {
+ final Object ensurePythonObject(Object object) {
+ if (ensurePythonObjectNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
- releaseNativeWrapperNode = insert(ReleaseNativeWrapperNodeGen.create());
+ ensurePythonObjectNode = insert(EnsurePythonObjectNode.create());
}
- return releaseNativeWrapperNode;
- }
-
- @Override
- public boolean isCloningAllowed() {
- return true;
+ return ensurePythonObjectNode.execute(PythonContext.get(this), object, false);
}
@Override
@@ -955,129 +664,161 @@ protected final Object readSelf(VirtualFrame frame) {
}
}
- public static class MethKeywordsRoot extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(true, 1, tsArray("self"), true, true);
- @Child protected ReadVarArgsNode readVarargsNode;
- @Child protected ReadVarKeywordsNode readKwargsNode;
- @Child protected CreateArgsTupleNode createArgsTupleNode;
- @Child protected ReleaseNativeSequenceStorageNode freeNode;
+ /**
+ * Base class for all native {@link PythonBuiltinClassType#PBuiltinFunction} (CPython type
+ * {@code PyMethodDescr_Type}) functions.
+ */
+ public abstract static class MethodDescriptorRoot extends WrapperBaseRoot {
+ @Child private GetThreadStateNode getThreadStateNode;
+ @Child private NativeToPythonReturnNode nativeToPythonReturnNode;
+ @Child private PyObjectCheckFunctionResultNode checkResultNode;
- protected boolean seenNativeArgsTupleStorage;
+ final CApiTiming timing;
- public MethKeywordsRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
- super(language, name, isStatic, provider);
- this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex());
- this.readKwargsNode = ReadVarKeywordsNode.create(SIGNATURE.varKeywordsPArgumentsIndex());
- this.createArgsTupleNode = CreateArgsTupleNodeGen.create();
- this.freeNode = ReleaseNativeSequenceStorageNodeGen.create();
+ MethodDescriptorRoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper wrapper) {
+ super(language, name, isStatic);
+ assert wrapper.returnValue == PyObjectReturn;
+ this.timing = CApiTiming.create(true, name);
}
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object self = readSelf(frame);
- Object[] args = readVarargsNode.execute(frame);
- PKeyword[] kwargs = readKwargsNode.execute(frame);
- PythonLanguage language = getLanguage(PythonLanguage.class);
- return new Object[]{self, createArgsTupleNode.execute(language, args, seenNativeArgsTupleStorage), kwargs.length > 0 ? PFactory.createDict(language, kwargs) : PNone.NO_VALUE};
+ final GetThreadStateNode ensureGetThreadStateNode() {
+ if (getThreadStateNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ getThreadStateNode = insert(GetThreadStateNode.create());
+ }
+ return getThreadStateNode;
}
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- boolean freed = MethVarargsRoot.releaseArgsTuple(cArguments[1], freeNode, seenNativeArgsTupleStorage);
- if (!seenNativeArgsTupleStorage && freed) {
- seenNativeArgsTupleStorage = true;
+ final Object nativeToPython(PythonContext context, long lresult) {
+ if (nativeToPythonReturnNode == null || checkResultNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ nativeToPythonReturnNode = insert(NativeToPythonReturnNode.create());
+ checkResultNode = insert(PyObjectCheckFunctionResultNodeGen.create());
}
- releaseNativeWrapperNode.execute(cArguments[2]);
+ return checkResultNode.execute(context, name, nativeToPythonReturnNode.executeRaw(lresult));
}
+ }
- @Override
- public Signature getSignature() {
- return SIGNATURE;
+ /**
+ * Base class for all native {@link PythonBuiltinClassType#WrapperDescriptor} (CPython type
+ * {@code PyWrapperDescr_Type}) functions. Those are used for the slot wrapper functions of C
+ * function type {@code wrapperfunc}.
+ */
+ public abstract static class WrapperDescriptorRoot extends WrapperBaseRoot {
+
+ private final BranchProfile exceptionProfile = BranchProfile.create();
+
+ WrapperDescriptorRoot(PythonLanguage language, TruffleString name, @SuppressWarnings("unused") PExternalFunctionWrapper wrapper) {
+ super(language, name, false);
+ }
+
+ protected final void transformExceptionFromNative() {
+ transformExceptionFromNative(true);
+ }
+
+ protected final void transformExceptionFromNative(boolean strict) {
+ exceptionProfile.enter();
+ transformExceptionFromNative(PythonContext.get(this), strict);
+ if (strict) {
+ throw CompilerDirectives.shouldNotReachHere();
+ }
+ }
+
+ @TruffleBoundary
+ private void transformExceptionFromNative(PythonContext context, boolean strict) {
+ PythonThreadState threadState = GetThreadStateNode.getUncached().executeCached(context);
+ TransformExceptionFromNativeNode.executeUncached(threadState, name, true, strict);
+ }
+ }
+
+ /**
+ * Base class for wrapper functions that return an object (i.e. {@code PyObject*}).
+ */
+ public abstract static class ObjectWrapperDescriptorRoot extends WrapperDescriptorRoot {
+
+ @Child private NativeToPythonReturnNode nativeToPythonReturnNode;
+
+ ObjectWrapperDescriptorRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper wrapper) {
+ super(language, name, wrapper);
+ assert wrapper.signature.returnValue == PyObjectReturn;
+ }
+
+ final NativeToPythonReturnNode ensureNativeToPythonReturnNode() {
+ if (nativeToPythonReturnNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ nativeToPythonReturnNode = insert(NativeToPythonReturnNode.create());
+ }
+ return nativeToPythonReturnNode;
+ }
+
+ final Object returnNativeObjectToPython(long lresult) {
+ Object result = ensureNativeToPythonReturnNode().executeRaw(lresult);
+ if (result == PNone.NO_VALUE) {
+ transformExceptionFromNative();
+ }
+ return result;
}
}
- public static final class MethVarargsRoot extends MethodDescriptorRoot {
+ public static final class MethVarargsRoot extends PyCFunctionRootNode {
private static final Signature SIGNATURE = createSignature(false, 1, tsArray("self"), true, true);
@Child private ReadVarArgsNode readVarargsNode;
- @Child private CreateArgsTupleNode createArgsTupleNode;
- @Child private ReleaseNativeSequenceStorageNode freeNode;
+ @Child private PythonToNativeNode argsTupleToNativeNode;
+ @Child private CreateNativeArgsTupleNode createNativeArgsTupleNode;
+ @Child private ReleaseNativeArgsTupleNode freeNode;
- private boolean seenNativeArgsTupleStorage;
+ @CompilationFinal private boolean seenNativeArgsTupleStorage;
- public MethVarargsRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
+ public MethVarargsRoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) {
super(language, name, isStatic, provider);
this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex());
- this.createArgsTupleNode = CreateArgsTupleNodeGen.create();
- this.freeNode = ReleaseNativeSequenceStorageNodeGen.create();
+ this.argsTupleToNativeNode = PythonToNativeNode.create();
+ this.createNativeArgsTupleNode = CreateNativeArgsTupleNodeGen.create();
+ this.freeNode = ReleaseNativeArgsTupleNodeGen.create();
}
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ PythonContext context = PythonContext.get(this);
Object self = readSelf(frame);
Object[] args = readVarargsNode.execute(frame);
- return new Object[]{self, createArgsTupleNode.execute(getLanguage(PythonLanguage.class), args, seenNativeArgsTupleStorage)};
- }
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- // releaseNativeWrapperNode.execute(cArguments[1]);
- boolean freed = releaseArgsTuple(cArguments[1], freeNode, seenNativeArgsTupleStorage);
- if (!seenNativeArgsTupleStorage && freed) {
- seenNativeArgsTupleStorage = true;
+ PTuple managedArgsTuple;
+ long argsTuplePtr;
+ if (seenNativeArgsTupleStorage) {
+ managedArgsTuple = null;
+ argsTuplePtr = createNativeArgsTupleNode.execute(context, args);
+ } else {
+ managedArgsTuple = PFactory.createTuple(context.getLanguage(this), args);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(managedArgsTuple);
+ argsTuplePtr = argsTupleToNativeNode.executeLong(managedArgsTuple);
}
- }
- static boolean releaseArgsTuple(Object argsTupleObject, ReleaseNativeSequenceStorageNode freeNode, boolean eagerNativeStorage) {
- if (!PythonContext.get(freeNode).isNativeAccessAllowed()) {
- return false;
- }
try {
- assert argsTupleObject instanceof PythonObjectNativeWrapper;
- PythonObjectNativeWrapper argsTupleWrapper = (PythonObjectNativeWrapper) argsTupleObject;
- Object argsTuple = argsTupleWrapper.getDelegate();
- assert argsTuple instanceof PTuple;
- SequenceStorage s = ((PTuple) argsTuple).getSequenceStorage();
- /*
- * This assumes that the common case is that the args tuple is still owned by the
- * runtime. However, it could be that the C extension does 'Py_INCREF(argsTuple)'
- * and in this case, we must not free the memory. Further, since we assumed that we
- * may free the memory after the call returned, we also need to create a
- * NativeSequenceStorageReference such that the NativeSequenceStorage will not leak.
- */
- if (s instanceof NativeSequenceStorage nativeSequenceStorage) {
- /*
- * TODO we would like to release the memory already, but we currently can't tell
- * if the args tuple escaped back to managed. So we always create the native
- * storage with an ownership reference and the following condition is always
- * true.
- */
- if (nativeSequenceStorage.hasReference()) {
- /*
- * Not allocated by this root. Note that this can happen even when
- * seenNativeArgsTupleStorage is true, because it could have been set by a
- * recursive invocation of this root.
- */
- return true;
- }
- assert eagerNativeStorage;
- if (argsTupleWrapper.getRefCount() == MANAGED_REFCNT) {
- // in this case, the runtime still exclusively owns the memory
- freeNode.execute(nativeSequenceStorage);
- } else {
- // the C ext also created a reference; no exclusive ownership
- CApiTransitions.registerNativeSequenceStorage(nativeSequenceStorage);
- }
- return true;
+ return invokeExternalFunction(frame, boundFunction, self, argsTuplePtr);
+ } finally {
+ assert managedArgsTuple != null || seenNativeArgsTupleStorage;
+ boolean hadNativeSequenceStorage = postprocessArgsTuple(managedArgsTuple, argsTuplePtr, args, freeNode);
+ if (!seenNativeArgsTupleStorage || hadNativeSequenceStorage) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ seenNativeArgsTupleStorage = true;
}
+ Reference.reachabilityFence(args);
+ }
+ }
+
+ public static boolean postprocessArgsTuple(PTuple managedArgsTuple, long argsTuplePtr, Object[] args,
+ ReleaseNativeArgsTupleNode releaseNativeArgsTupleNode) {
+ if (managedArgsTuple == null) {
+ releaseNativeArgsTupleNode.execute(argsTuplePtr, args);
return false;
- } catch (ClassCastException e) {
- // cut exception edge
- throw CompilerDirectives.shouldNotReachHere(e);
}
+ /*
+ * Note: 'seenNativeArgsTupleStorage' is not necessarily 'false' in this case because a
+ * recursive invocation may have already set it to 'true' in the meantime.
+ */
+ assert !(managedArgsTuple.getSequenceStorage() instanceof NativeSequenceStorage nativeSequenceStorage) || nativeSequenceStorage.hasReference();
+ return managedArgsTuple.getSequenceStorage() instanceof NativeSequenceStorage;
}
@Override
@@ -1086,41 +827,81 @@ public Signature getSignature() {
}
}
- public static final class MethNewRoot extends MethKeywordsRoot {
+ static final class MethKeywordsRoot extends MethodDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(true, 1, tsArray("self"), true, true);
- public MethNewRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
- super(language, name, isStatic, provider);
- }
+ @Child private ReadVarArgsNode readVarargsNode;
+ @Child private ReadVarKeywordsNode readKwargsNode;
+ @Child private CreateNativeArgsTupleNode createNativeArgsTupleNode;
+ @Child private ReleaseNativeArgsTupleNode freeNode;
+ @Child private CalleeContext calleeContext;
+ @Child private BoundaryCallData boundaryCallData;
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object methodSelf = readSelf(frame);
- Object[] args = readVarargsNode.execute(frame);
- // TODO checks
- Object self = args[0];
- args = PythonUtils.arrayCopyOfRange(args, 1, args.length);
- PKeyword[] kwargs = readKwargsNode.execute(frame);
- PythonLanguage language = getLanguage(PythonLanguage.class);
- return new Object[]{self, createArgsTupleNode.execute(language, args, seenNativeArgsTupleStorage), kwargs.length > 0 ? PFactory.createDict(language, kwargs) : PNone.NO_VALUE};
- }
- }
+ @Child private PythonToNativeNode selfToNativeNode;
+ @Child private PythonToNativeNode argsToNativeNode;
+ @Child private PythonToNativeNode kwargsToNativeNode;
- public static final class MethInquiryRoot extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self"), true, false);
+ @CompilationFinal private boolean seenNativeArgsTupleStorage;
- public MethInquiryRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
+ public MethKeywordsRoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) {
super(language, name, isStatic, provider);
}
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- return new Object[]{readSelf(frame)};
- }
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ if (calleeContext == null || boundaryCallData == null || selfToNativeNode == null || argsToNativeNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ createNodes();
+ }
+ PythonContext context = PythonContext.get(this);
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ensureReleaseNativeWrapperNode().execute(cArguments[0]);
- }
+ Object self = readSelf(frame);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
+
+ Object[] args = readVarargsNode.execute(frame);
+ PTuple managedArgsTuple;
+ long argsTuplePtr;
+ if (seenNativeArgsTupleStorage) {
+ managedArgsTuple = null;
+ argsTuplePtr = createNativeArgsTupleNode.execute(context, args);
+ } else {
+ managedArgsTuple = PFactory.createTuple(context.getLanguage(this), args);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(managedArgsTuple);
+ argsTuplePtr = argsToNativeNode.executeLong(managedArgsTuple);
+ }
+
+ PKeyword[] kwargs = readKwargsNode.execute(frame);
+ Object kwargsDict = kwargs.length > 0 ? PFactory.createDict(context.getLanguage(), kwargs) : PNone.NO_VALUE;
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(kwargsDict);
+
+ try {
+ long l = ExternalFunctionInvoker.invokePYCFUNCTION_WITH_KEYWORDS(frame, timing, context.ensureNativeContext(), boundaryCallData, ensureGetThreadStateNode().executeCached(context),
+ boundFunction, selfToNativeNode.executeLong(self), argsTuplePtr, kwargsToNativeNode.executeLong(kwargsDict));
+ return nativeToPython(context, l);
+ } finally {
+ Reference.reachabilityFence(self);
+ boolean hadNativeSequenceStorage = MethVarargsRoot.postprocessArgsTuple(managedArgsTuple, argsTuplePtr, args, freeNode);
+ if (!seenNativeArgsTupleStorage && hadNativeSequenceStorage) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ seenNativeArgsTupleStorage = true;
+ }
+ Reference.reachabilityFence(args);
+ Reference.reachabilityFence(kwargsDict);
+ }
+ }
+
+ private void createNodes() {
+ CompilerAsserts.neverPartOfCompilation();
+ readVarargsNode = insert(ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex()));
+ readKwargsNode = insert(ReadVarKeywordsNode.create(SIGNATURE.varKeywordsPArgumentsIndex()));
+ createNativeArgsTupleNode = insert(CreateNativeArgsTupleNodeGen.create());
+ freeNode = insert(ReleaseNativeArgsTupleNodeGen.create());
+ calleeContext = insert(CalleeContext.create());
+ boundaryCallData = insert(BoundaryCallData.createFor(this));
+ selfToNativeNode = insert(PythonToNativeNode.create());
+ argsToNativeNode = insert(PythonToNativeNode.create());
+ kwargsToNativeNode = insert(PythonToNativeNode.create());
+ }
@Override
public Signature getSignature() {
@@ -1128,21 +909,47 @@ public Signature getSignature() {
}
}
- public static final class MethNoargsRoot extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self"), true, true);
+ /** Implements semantics of {@code typeobject.c: wrap_unaryfunc}. */
+ @CApiWrapperDescriptor(value = UNARYFUNC)
+ abstract static class MethUnaryFunc extends ObjectWrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self"), true, false);
- public MethNoargsRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
- super(language, name, isStatic, provider);
+ protected MethUnaryFunc(PythonLanguage lang, TruffleString name, PExternalFunctionWrapper provider) {
+ super(lang, name, provider);
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.UNARYFUNC, argConversions = {PythonToNativeNode.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- return new Object[]{readSelf(frame), PNone.NO_VALUE};
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self));
}
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ensureReleaseNativeWrapperNode().execute(cArguments[0]);
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
+ }
+
+ public abstract static class MethNewOrCallRoot extends ObjectWrapperDescriptorRoot {
+ private static final Signature SIGNATURE = MethKeywordsRoot.SIGNATURE;
+ @Child ReadVarArgsNode readVarargsNode;
+ @Child ReadVarKeywordsNode readKwargsNode;
+ @Child PythonToNativeNode argsTupleToNativeNode;
+ @Child CreateNativeArgsTupleNode createNativeArgsTupleNode;
+ @Child ReleaseNativeArgsTupleNode freeNode;
+
+ @CompilationFinal boolean seenNativeArgsTupleStorage;
+
+ public MethNewOrCallRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex());
+ this.readKwargsNode = ReadVarKeywordsNode.create(SIGNATURE.varKeywordsPArgumentsIndex());
+ this.argsTupleToNativeNode = PythonToNativeNode.create();
+ this.createNativeArgsTupleNode = CreateNativeArgsTupleNodeGen.create();
+ this.freeNode = ReleaseNativeArgsTupleNodeGen.create();
}
@Override
@@ -1151,27 +958,265 @@ public Signature getSignature() {
}
}
- public static final class MethORoot extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "arg"), true, true);
- @Child private ReadIndexedArgumentNode readArgNode;
+ @CApiWrapperDescriptor(value = NEW)
+ abstract static class MethNewRoot extends MethNewOrCallRoot {
+
+ public MethNewRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ }
+
+ @InvokeExternalFunction(value = ExternalFunctionSignature.NEWFUNC, argConversions = {PythonToNativeNode.class, long.class, PythonToNativeNode.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long argsTuplePtr, Object kwds);
+
+ @Override
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ PythonContext context = PythonContext.get(this);
+ Object[] args = readVarargsNode.execute(frame);
+
+ // TODO checks
+ Object self = args[0];
+
+ args = PythonUtils.arrayCopyOfRange(args, 1, args.length);
+ PTuple managedArgsTuple;
+ long argsTuplePtr;
+ if (seenNativeArgsTupleStorage) {
+ managedArgsTuple = null;
+ argsTuplePtr = createNativeArgsTupleNode.execute(context, args);
+ } else {
+ managedArgsTuple = PFactory.createTuple(context.getLanguage(this), args);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(managedArgsTuple);
+ argsTuplePtr = argsTupleToNativeNode.executeLong(managedArgsTuple);
+ }
+
+ PKeyword[] kwargs = readKwargsNode.execute(frame);
+ PythonLanguage language = getLanguage(PythonLanguage.class);
+ Object kwargsDict = kwargs.length > 0 ? PFactory.createDict(language, kwargs) : PNone.NO_VALUE;
+
+ try {
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, argsTuplePtr, kwargsDict));
+ } finally {
+ boolean hadNativeSequenceStorage = MethVarargsRoot.postprocessArgsTuple(managedArgsTuple, argsTuplePtr, args, freeNode);
+ if (!seenNativeArgsTupleStorage && hadNativeSequenceStorage) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ seenNativeArgsTupleStorage = true;
+ }
+ Reference.reachabilityFence(args);
+ }
+ }
+ }
+
+ @CApiWrapperDescriptor(value = CALL)
+ abstract static class MethCallRoot extends MethNewOrCallRoot {
+
+ public MethCallRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ }
+
+ @InvokeExternalFunction(value = ExternalFunctionSignature.TERNARYFUNC, argConversions = {PythonToNativeNode.class, long.class, PythonToNativeNode.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long args, Object kwds);
+
+ @Override
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ PythonContext context = PythonContext.get(this);
+
+ Object self = readSelf(frame);
+
+ Object[] args = readVarargsNode.execute(frame);
+ PTuple managedArgsTuple;
+ long argsTuplePtr;
+ if (seenNativeArgsTupleStorage) {
+ managedArgsTuple = null;
+ argsTuplePtr = createNativeArgsTupleNode.execute(context, args);
+ } else {
+ managedArgsTuple = PFactory.createTuple(context.getLanguage(this), args);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(managedArgsTuple);
+ argsTuplePtr = argsTupleToNativeNode.executeLong(managedArgsTuple);
+ }
+
+ PKeyword[] kwargs = readKwargsNode.execute(frame);
+ Object kwargsDict = kwargs.length > 0 ? PFactory.createDict(context.getLanguage(), kwargs) : PNone.NO_VALUE;
+
+ try {
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, argsTuplePtr, kwargsDict));
+ } finally {
+ boolean hadNativeSequenceStorage = MethVarargsRoot.postprocessArgsTuple(managedArgsTuple, argsTuplePtr, args, freeNode);
+ if (!seenNativeArgsTupleStorage && hadNativeSequenceStorage) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ seenNativeArgsTupleStorage = true;
+ }
+ Reference.reachabilityFence(args);
+ }
+ }
+ }
+
+ @CApiWrapperDescriptor(value = INIT)
+ abstract static class MethInitRoot extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = MethKeywordsRoot.SIGNATURE;
- public MethORoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
+ @Child private ReadVarArgsNode readVarargsNode;
+ @Child private ReadVarKeywordsNode readKwargsNode;
+ @Child private PythonToNativeNode argsTupleToNativeNode;
+ @Child private CreateNativeArgsTupleNode createNativeArgsTupleNode;
+ @Child private ReleaseNativeArgsTupleNode freeNode;
+
+ @CompilationFinal boolean seenNativeArgsTupleStorage;
+
+ public MethInitRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex());
+ this.readKwargsNode = ReadVarKeywordsNode.create(SIGNATURE.varKeywordsPArgumentsIndex());
+ this.argsTupleToNativeNode = PythonToNativeNode.create();
+ this.createNativeArgsTupleNode = CreateNativeArgsTupleNodeGen.create();
+ this.freeNode = ReleaseNativeArgsTupleNodeGen.create();
+ }
+
+ @InvokeExternalFunction(value = ExternalFunctionSignature.INITPROC, retConversion = int.class, argConversions = {PythonToNativeNode.class, long.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long argsTuplePtr, Object kwds);
+
+ @Override
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ PythonContext context = PythonContext.get(this);
+
+ Object self = readSelf(frame);
+
+ Object[] args = readVarargsNode.execute(frame);
+ PTuple managedArgsTuple;
+ long argsTuplePtr;
+ if (seenNativeArgsTupleStorage) {
+ managedArgsTuple = null;
+ argsTuplePtr = createNativeArgsTupleNode.execute(context, args);
+ } else {
+ managedArgsTuple = PFactory.createTuple(context.getLanguage(this), args);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(managedArgsTuple);
+ argsTuplePtr = argsTupleToNativeNode.executeLong(managedArgsTuple);
+ }
+
+ PKeyword[] kwargs = readKwargsNode.execute(frame);
+ Object kwargsDict = kwargs.length > 0 ? PFactory.createDict(context.getLanguage(), kwargs) : PNone.NO_VALUE;
+
+ try {
+ if (invokeExternalFunction(frame, boundFunction, self, argsTuplePtr, kwargsDict) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
+ } finally {
+ boolean hadNativeSequenceStorage = MethVarargsRoot.postprocessArgsTuple(managedArgsTuple, argsTuplePtr, args, freeNode);
+ if (!seenNativeArgsTupleStorage && hadNativeSequenceStorage) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ seenNativeArgsTupleStorage = true;
+ }
+ Reference.reachabilityFence(args);
+ }
+ }
+
+ @Override
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
+ }
+
+ /** Implements semantics of {@code typeobject.c: wrap_inquirypred}. */
+ @CApiWrapperDescriptor(value = INQUIRYPRED)
+ public abstract static class MethInquiryRoot extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self"), true, false);
+
+ public MethInquiryRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ }
+
+ @InvokeExternalFunction(value = ExternalFunctionSignature.INQUIRY, retConversion = int.class, argConversions = {PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self);
+
+ @Override
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ int result = invokeExternalFunction(frame, boundFunction, self);
+ if (result == -1) {
+ transformExceptionFromNative(false);
+ }
+ return result != 0;
+ }
+
+ @Override
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
+ }
+
+ static final class MethNoargsRoot extends PyCFunctionRootNode {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self"), true, true);
+
+ public MethNoargsRoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) {
super(language, name, isStatic, provider);
- this.readArgNode = ReadIndexedArgumentNode.create(1);
}
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
Object self = readSelf(frame);
- Object arg = readArgNode.execute(frame);
- return new Object[]{self, arg};
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
+ return invokeExternalFunction(frame, boundFunction, self, NULLPTR);
}
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
+ }
+
+ abstract static class PyCFunctionRootNode extends MethodDescriptorRoot {
+ @Child private CalleeContext calleeContext;
+ @Child private BoundaryCallData boundaryCallData;
+
+ @Child private PythonToNativeNode selfToNativeNode;
+
+ public PyCFunctionRootNode(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) {
+ super(language, name, isStatic, provider);
+ }
+
+ final Object invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long arg) {
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
+ if (calleeContext == null || boundaryCallData == null || selfToNativeNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ createNodes();
+ }
+ PythonContext context = PythonContext.get(this);
+ try {
+ long l = ExternalFunctionInvoker.invokePYCFUNCTION(frame, timing, context.ensureNativeContext(), boundaryCallData, ensureGetThreadStateNode().executeCached(context), boundFunction,
+ selfToNativeNode.executeLong(self), arg);
+ return nativeToPython(context, l);
+ } finally {
+ Reference.reachabilityFence(self);
+ }
+ }
+
+ private void createNodes() {
+ CompilerAsserts.neverPartOfCompilation();
+ calleeContext = insert(CalleeContext.create());
+ boundaryCallData = insert(BoundaryCallData.createFor(this));
+ selfToNativeNode = insert(PythonToNativeNode.create());
+ }
+ }
+
+ public static final class MethORoot extends PyCFunctionRootNode {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "arg"), true, true);
+ @Child private ReadIndexedArgumentNode readArgNode;
+ @Child private PythonToNativeNode argToNativeNode;
+
+ public MethORoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) {
+ super(language, name, isStatic, provider);
+ this.readArgNode = ReadIndexedArgumentNode.create(1);
+ this.argToNativeNode = PythonToNativeNode.create();
+ }
+
+ @Override
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object arg = ensurePythonObject(readArgNode.execute(frame));
+ try {
+ return invokeExternalFunction(frame, boundFunction, self, argToNativeNode.executeLong(arg));
+ } finally {
+ Reference.reachabilityFence(arg);
+ }
}
@Override
@@ -1180,44 +1225,76 @@ public Signature getSignature() {
}
}
- public static final class MethFastcallWithKeywordsRoot extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(true, 1, tsArray("self"), true, true);
+ static final class MethFastcallWithKeywordsRoot extends MethodDescriptorRoot {
+ /*
+ * METH_KEYWORDS and METH_FASTCALL|METH_KEYWORDS have the same Python-level signature but
+ * invoke a different C function signature.
+ */
+ private static final Signature SIGNATURE = MethKeywordsRoot.SIGNATURE;
+
@Child private ReadVarArgsNode readVarargsNode;
@Child private ReadVarKeywordsNode readKwargsNode;
+ @Child private BoundaryCallData boundaryCallData;
- public MethFastcallWithKeywordsRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
+ @Child private PythonToNativeNode argToNativeNode;
+ @Child private PythonToNativeNode kwNamesToNativeNode;
+
+ public MethFastcallWithKeywordsRoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) {
super(language, name, isStatic, provider);
- this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex());
- this.readKwargsNode = ReadVarKeywordsNode.create(SIGNATURE.varKeywordsPArgumentsIndex());
}
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ if (readVarargsNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ assert readKwargsNode == null;
+ assert boundaryCallData == null;
+ assert argToNativeNode == null;
+ assert kwNamesToNativeNode == null;
+ createNodes();
+ }
+ PythonContext context = PythonContext.get(this);
+
Object self = readSelf(frame);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
+
Object[] args = readVarargsNode.execute(frame);
PKeyword[] kwargs = readKwargsNode.execute(frame);
Object[] fastcallArgs = new Object[args.length + kwargs.length];
Object kwnamesTuple = PNone.NO_VALUE;
- PythonUtils.arraycopy(args, 0, fastcallArgs, 0, args.length);
+ for (int i = 0; i < args.length; i++) {
+ fastcallArgs[i] = ensurePythonObject(args[i]);
+ }
// Note: PyO3 doesn't like it when we put an empty tuple there if there are no args
if (kwargs.length > 0) {
Object[] fastcallKwnames = new Object[kwargs.length];
for (int i = 0; i < kwargs.length; i++) {
fastcallKwnames[i] = kwargs[i].getName();
- fastcallArgs[args.length + i] = kwargs[i].getValue();
+ fastcallArgs[args.length + i] = ensurePythonObject(kwargs[i].getValue());
}
- kwnamesTuple = PFactory.createTuple(PythonLanguage.get(this), fastcallKwnames);
+ kwnamesTuple = PFactory.createTuple(context.getLanguage(), fastcallKwnames);
+ }
+ long nativeFastcallArgs = MethFastcallRoot.createFastcallArgsArray(fastcallArgs, argToNativeNode);
+
+ try {
+ long l = ExternalFunctionInvoker.invokePYCFUNCTION_FAST_WITH_KEYWORDS(frame, timing, context.ensureNativeContext(), boundaryCallData, ensureGetThreadStateNode().executeCached(context),
+ boundFunction, argToNativeNode.executeLong(self), nativeFastcallArgs, args.length, kwNamesToNativeNode.executeLong(kwnamesTuple));
+ return nativeToPython(context, l);
+ } finally {
+ MethFastcallRoot.freeFastcallArgsArray(nativeFastcallArgs);
+ Reference.reachabilityFence(self);
+ Reference.reachabilityFence(fastcallArgs);
+ Reference.reachabilityFence(kwnamesTuple);
}
- return new Object[]{self, new CPyObjectArrayWrapper(fastcallArgs), args.length, kwnamesTuple};
}
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- CPyObjectArrayWrapper wrapper = (CPyObjectArrayWrapper) cArguments[1];
- wrapper.free(ensureReleaseNativeWrapperNode());
- releaseNativeWrapperNode.execute(cArguments[3]);
+ private void createNodes() {
+ CompilerAsserts.neverPartOfCompilation();
+ readVarargsNode = insert(ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex()));
+ readKwargsNode = insert(ReadVarKeywordsNode.create(SIGNATURE.varKeywordsPArgumentsIndex()));
+ boundaryCallData = insert(BoundaryCallData.createFor(this));
+ argToNativeNode = insert(PythonToNativeNode.create());
+ kwNamesToNativeNode = insert(PythonToNativeNode.create());
}
@Override
@@ -1226,43 +1303,76 @@ public Signature getSignature() {
}
}
- public static final class MethMethodRoot extends MethodDescriptorRoot {
+ static final class MethMethodRoot extends MethodDescriptorRoot {
private static final Signature SIGNATURE = createSignature(true, 1, tsArray("self", "cls"), true, true);
+
@Child private ReadIndexedArgumentNode readClsNode;
@Child private ReadVarArgsNode readVarargsNode;
@Child private ReadVarKeywordsNode readKwargsNode;
- public MethMethodRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
+ @Child private BoundaryCallData boundaryCallData;
+ @Child private PythonToNativeNode argToNativeNode;
+ @Child private PythonToNativeNode kwNamesToNativeNode;
+
+ public MethMethodRoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) {
super(language, name, isStatic, provider);
- this.readClsNode = ReadIndexedArgumentNode.create(1);
- this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex());
- this.readKwargsNode = ReadVarKeywordsNode.create(SIGNATURE.varKeywordsPArgumentsIndex());
}
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ if (readClsNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ assert readVarargsNode == null;
+ assert readKwargsNode == null;
+ assert boundaryCallData == null;
+ assert argToNativeNode == null;
+ assert kwNamesToNativeNode == null;
+ createNodes();
+ }
+ PythonContext context = PythonContext.get(this);
+
Object self = readSelf(frame);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
+
Object cls = readClsNode.execute(frame);
Object[] args = readVarargsNode.execute(frame);
PKeyword[] kwargs = readKwargsNode.execute(frame);
Object[] fastcallArgs = new Object[args.length + kwargs.length];
- Object[] fastcallKwnames = new Object[kwargs.length];
- PythonUtils.arraycopy(args, 0, fastcallArgs, 0, args.length);
- for (int i = 0; i < kwargs.length; i++) {
- fastcallKwnames[i] = kwargs[i].getName();
- fastcallArgs[args.length + i] = kwargs[i].getValue();
+ Object kwnamesTuple = PNone.NO_VALUE;
+ for (int i = 0; i < args.length; i++) {
+ fastcallArgs[i] = ensurePythonObject(args[i]);
+ }
+ // Note: PyO3 doesn't like it when we put an empty tuple there if there are no args
+ if (kwargs.length > 0) {
+ Object[] fastcallKwnames = new Object[kwargs.length];
+ for (int i = 0; i < kwargs.length; i++) {
+ fastcallKwnames[i] = kwargs[i].getName();
+ fastcallArgs[args.length + i] = ensurePythonObject(kwargs[i].getValue());
+ }
+ kwnamesTuple = PFactory.createTuple(context.getLanguage(), fastcallKwnames);
+ }
+ long nativeFastcallArgs = MethFastcallRoot.createFastcallArgsArray(fastcallArgs, argToNativeNode);
+
+ try {
+ long l = ExternalFunctionInvoker.invokePYCMETHOD(frame, timing, context.ensureNativeContext(), boundaryCallData, ensureGetThreadStateNode().executeCached(context),
+ boundFunction, argToNativeNode.executeLong(self), argToNativeNode.executeLong(cls), nativeFastcallArgs, args.length, kwNamesToNativeNode.executeLong(kwnamesTuple));
+ return nativeToPython(context, l);
+ } finally {
+ MethFastcallRoot.freeFastcallArgsArray(nativeFastcallArgs);
+ Reference.reachabilityFence(self);
+ Reference.reachabilityFence(fastcallArgs);
+ Reference.reachabilityFence(kwnamesTuple);
}
- return new Object[]{self, cls, new CPyObjectArrayWrapper(fastcallArgs), args.length, PFactory.createTuple(PythonLanguage.get(this), fastcallKwnames)};
}
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
- CPyObjectArrayWrapper wrapper = (CPyObjectArrayWrapper) cArguments[2];
- wrapper.free(releaseNativeWrapperNode);
- releaseNativeWrapperNode.execute(cArguments[4]);
+ private void createNodes() {
+ CompilerAsserts.neverPartOfCompilation();
+ readClsNode = insert(ReadIndexedArgumentNode.create(1));
+ readVarargsNode = insert(ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex()));
+ readKwargsNode = insert(ReadVarKeywordsNode.create(SIGNATURE.varKeywordsPArgumentsIndex()));
+ boundaryCallData = insert(BoundaryCallData.createFor(this));
+ argToNativeNode = insert(PythonToNativeNode.create());
+ kwNamesToNativeNode = insert(PythonToNativeNode.create());
}
@Override
@@ -1271,28 +1381,86 @@ public Signature getSignature() {
}
}
- public static final class MethFastcallRoot extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, 1, tsArray("self"), true, true);
+ static final class MethFastcallRoot extends MethodDescriptorRoot {
+ /*
+ * METH_VARARGS and METH_FASTCALL have the same Python-level signature but invoke a
+ * different C function signature.
+ */
+ private static final Signature SIGNATURE = MethVarargsRoot.SIGNATURE;
+
@Child private ReadVarArgsNode readVarargsNode;
+ @Child private BoundaryCallData boundaryCallData;
+
+ @Child private PythonToNativeNode argToNativeNode;
- public MethFastcallRoot(PythonLanguage language, TruffleString name, boolean isStatic, PExternalFunctionWrapper provider) {
+ public MethFastcallRoot(PythonLanguage language, TruffleString name, boolean isStatic, MethodDescriptorWrapper provider) {
super(language, name, isStatic, provider);
- this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex());
}
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ if (readVarargsNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ assert boundaryCallData == null;
+ assert argToNativeNode == null;
+ createNodes();
+ }
+ PythonContext context = PythonContext.get(this);
+
Object self = readSelf(frame);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
+
Object[] args = readVarargsNode.execute(frame);
- return new Object[]{self, new CPyObjectArrayWrapper(args), args.length};
+ Object[] promotedArgs = new Object[args.length];
+ for (int i = 0; i < args.length; i++) {
+ promotedArgs[i] = ensurePythonObject(args[i]);
+ }
+ long argsArray = createFastcallArgsArray(promotedArgs, argToNativeNode);
+
+ try {
+ long l = ExternalFunctionInvoker.invokePYCFUNCTION_FAST(frame, timing, context.ensureNativeContext(), boundaryCallData, ensureGetThreadStateNode().executeCached(context),
+ boundFunction, argToNativeNode.executeLong(self), argsArray, promotedArgs.length);
+ return nativeToPython(context, l);
+ } finally {
+ freeFastcallArgsArray(argsArray);
+ Reference.reachabilityFence(self);
+ Reference.reachabilityFence(promotedArgs);
+ }
}
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- CPyObjectArrayWrapper wrapper = (CPyObjectArrayWrapper) cArguments[1];
- wrapper.free(ensureReleaseNativeWrapperNode());
+ /**
+ * Transforms an {@code Object[]} containing Python objects to a native
+ * {@code PyObject *arr[]}. This will not create new {@code PyObject *} references (i.e.
+ * refcount is not increased).
+ */
+ static long createFastcallArgsArray(Object[] data, PythonToNativeNode argToNativeNode) {
+ if (data.length == 0) {
+ return NULLPTR;
+ }
+ assert PythonContext.get(null).isNativeAccessAllowed();
+ long ptr = NativeMemory.malloc((long) data.length * NativeMemory.POINTER_SIZE);
+ for (int i = 0; i < data.length; i++) {
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(data[i]);
+ NativeMemory.writePtrArrayElement(ptr, i, argToNativeNode.executeLong(data[i]));
+ }
+ return ptr;
+ }
+
+ static void freeFastcallArgsArray(long pointer) {
+ if (pointer == NULLPTR) {
+ return;
+ }
+
+ // TODO we currently don't implement immediate releases of native objects.
+ assert PythonContext.get(null).isNativeAccessAllowed();
+ NativeMemory.free(pointer);
+ }
+
+ private void createNodes() {
+ CompilerAsserts.neverPartOfCompilation();
+ readVarargsNode = insert(ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex()));
+ boundaryCallData = insert(BoundaryCallData.createFor(this));
+ argToNativeNode = insert(PythonToNativeNode.create());
}
@Override
@@ -1301,34 +1469,27 @@ public Signature getSignature() {
}
}
- /**
- * Wrapper root node for C function type {@code allocfunc} and {@code ssizeargfunc}.
- */
- static class AllocFuncRootNode extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "nitems"), true, false);
- @Child private ReadIndexedArgumentNode readArgNode;
- @Child private ConvertPIntToPrimitiveNode asSsizeTNode;
+ /** Implements semantics of {@code typeobject.c: wrap_indexargfunc}. */
+ @CApiWrapperDescriptor(value = INDEXARGFUNC)
+ public abstract static class IndexArgFuncRootNode extends ObjectWrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "i"), true, false);
+ @Child private ReadIndexedArgumentNode readINode;
+ @Child private PyNumberAsSizeNode asSizeNode;
- AllocFuncRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
- this.readArgNode = ReadIndexedArgumentNode.create(1);
- this.asSsizeTNode = ConvertPIntToPrimitiveNodeGen.create();
+ IndexArgFuncRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ this.readINode = ReadIndexedArgumentNode.create(1);
+ this.asSizeNode = PyNumberAsSizeNode.create();
}
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object self = readSelf(frame);
- Object arg = readArgNode.execute(frame);
- try {
- return new Object[]{self, asSsizeTNode.executeLongCached(arg, 1, Long.BYTES)};
- } catch (UnexpectedResultException e) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- }
+ @InvokeExternalFunction(value = ExternalFunctionSignature.SSIZEARGFUNC, argConversions = {PythonToNativeNode.class, long.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long i);
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ensureReleaseNativeWrapperNode().execute(cArguments[0]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ long i = asSizeNode.executeExactCached(frame, readINode.execute(frame));
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, i));
}
@Override
@@ -1340,32 +1501,34 @@ public Signature getSignature() {
/**
* Wrapper root node for a get attribute function (C type {@code getattrfunc}).
*/
- static final class GetAttrFuncRootNode extends MethodDescriptorRoot {
+ @CApiWrapperDescriptor(value = GETATTR)
+ public abstract static class GetAttrFuncRootNode extends ObjectWrapperDescriptorRoot {
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(GetAttrFuncRootNode.class);
private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "key"), true, false);
@Child private ReadIndexedArgumentNode readArgNode;
- @Child private CExtNodes.AsCharPointerNode asCharPointerNode;
- @Child private CStructAccess.FreeNode free;
+ @Child private AsCharPointerNode asCharPointerNode;
GetAttrFuncRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ super(language, name, provider);
this.readArgNode = ReadIndexedArgumentNode.create(1);
this.asCharPointerNode = AsCharPointerNodeGen.create();
- this.free = CStructAccess.FreeNode.create();
}
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object self = readSelf(frame);
- Object arg = readArgNode.execute(frame);
- // TODO we should use 'CStringWrapper' for 'arg' but it does currently not support
- // PString
- return new Object[]{self, asCharPointerNode.execute(arg)};
- }
+ @InvokeExternalFunction(value = ExternalFunctionSignature.GETATTRFUNC, argConversions = {PythonToNativeNode.class, long.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long key);
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ensureReleaseNativeWrapperNode().execute(cArguments[0]);
- free.free(cArguments[1]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ long key = asCharPointerNode.execute(readArgNode.execute(frame));
+ try {
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, key));
+ } finally {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Freeing name (const char *)0x%x", key));
+ }
+ free(key);
+ }
}
@Override
@@ -1377,37 +1540,74 @@ public Signature getSignature() {
/**
* Wrapper root node for a set attribute function (C type {@code setattrfunc}).
*/
- static final class SetAttrFuncRootNode extends MethodDescriptorRoot {
+ @CApiWrapperDescriptor(value = SETATTR)
+ public abstract static class SetAttrFuncRootNode extends ObjectWrapperDescriptorRoot {
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(SetAttrFuncRootNode.class);
private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "key", "value"), true, false);
@Child private ReadIndexedArgumentNode readArg1Node;
@Child private ReadIndexedArgumentNode readArg2Node;
- @Child private CExtNodes.AsCharPointerNode asCharPointerNode;
- @Child private CStructAccess.FreeNode free;
+ @Child private AsCharPointerNode asCharPointerNode;
SetAttrFuncRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ super(language, name, provider);
this.readArg1Node = ReadIndexedArgumentNode.create(1);
this.readArg2Node = ReadIndexedArgumentNode.create(2);
this.asCharPointerNode = AsCharPointerNodeGen.create();
- this.free = CStructAccess.FreeNode.create();
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.SETATTRFUNC, retConversion = int.class, argConversions = {PythonToNativeNode.class, long.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long key, Object value);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
Object self = readSelf(frame);
- Object arg1 = readArg1Node.execute(frame);
- Object arg2 = readArg2Node.execute(frame);
- // TODO we should use 'CStringWrapper' for 'arg1' but it does currently not support
- // PString
- return new Object[]{self, asCharPointerNode.execute(arg1), arg2};
+ long key = asCharPointerNode.execute(readArg1Node.execute(frame));
+ Object value = ensurePythonObject(readArg2Node.execute(frame));
+
+ try {
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, key, value));
+ } finally {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Freeing name (const char *)0x%x", key));
+ }
+ free(key);
+ }
+ }
+
+ @Override
+ public Signature getSignature() {
+ return SIGNATURE;
}
+ }
+
+ /** Implements semantics of {@code typeobject.c: wrap_setattr} */
+ @CApiWrapperDescriptor(value = SETATTRO)
+ public abstract static class SetAttrOFuncRootNode extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "name", "value"), true, false);
+ @Child private ReadIndexedArgumentNode readNameNode;
+ @Child private ReadIndexedArgumentNode readValueNode;
+
+ SetAttrOFuncRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ this.readNameNode = ReadIndexedArgumentNode.create(1);
+ this.readValueNode = ReadIndexedArgumentNode.create(2);
+ }
+
+ @InvokeExternalFunction(value = ExternalFunctionSignature.SETATTROFUNC, retConversion = int.class, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class,
+ PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object name, Object value);
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- free.free(cArguments[1]);
- releaseNativeWrapperNode.execute(cArguments[2]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
+ Object name = ensurePythonObject(readNameNode.execute(frame));
+ Object value = ensurePythonObject(readValueNode.execute(frame));
+
+ if (invokeExternalFunction(frame, boundFunction, self, name, value) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
}
@Override
@@ -1417,71 +1617,68 @@ public Signature getSignature() {
}
/**
- * Wrapper root node for a rich compare function (C type {@code richcmpfunc}).
+ * Wrapper root node for a rich compare function (C type {@code richcmpfunc}). There is no
+ * equivalent wrapper function in CPython but this is needed to be able to call
+ * {@code tp_richcompare}.
*/
- static final class RichCmpFuncRootNode extends MethodDescriptorRoot {
+ @CApiWrapperDescriptor(value = RICHCMP)
+ public abstract static class RichCmpFuncRootNode extends ObjectWrapperDescriptorRoot {
private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "other", "op"), true, false);
- @Child private ReadIndexedArgumentNode readArg1Node;
- @Child private ReadIndexedArgumentNode readArg2Node;
+ @Child private ReadIndexedArgumentNode readOtherNode;
+ @Child private ReadIndexedArgumentNode readOpNode;
@Child private ConvertPIntToPrimitiveNode asSsizeTNode;
RichCmpFuncRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
- this.readArg1Node = ReadIndexedArgumentNode.create(1);
- this.readArg2Node = ReadIndexedArgumentNode.create(2);
+ super(language, name, provider);
+ this.readOtherNode = ReadIndexedArgumentNode.create(1);
+ this.readOpNode = ReadIndexedArgumentNode.create(2);
this.asSsizeTNode = ConvertPIntToPrimitiveNodeGen.create();
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.RICHCMPFUNC, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, int.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object name, int op);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
try {
Object self = readSelf(frame);
- Object arg1 = readArg1Node.execute(frame);
- Object arg2 = readArg2Node.execute(frame);
- return new Object[]{self, arg1, asSsizeTNode.executeIntCached(arg2, 1, Integer.BYTES)};
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
+ Object arg1 = ensurePythonObject(readOtherNode.execute(frame));
+ Object arg2 = ensurePythonObject(readOpNode.execute(frame));
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, arg1, asSsizeTNode.executeIntCached(arg2, 1, Integer.BYTES)));
} catch (UnexpectedResultException e) {
throw CompilerDirectives.shouldNotReachHere();
}
}
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
- }
-
@Override
public Signature getSignature() {
return SIGNATURE;
}
}
- /**
- * Implements semantics of {@code typeobject.c: wrap_sq_item}.
- */
- // TODO: can we remove this???
- static final class GetItemRootNode extends MethodDescriptorRoot {
+ /** Implements semantics of {@code typeobject.c: wrap_sq_item}. */
+ @CApiWrapperDescriptor(value = SQ_ITEM)
+ public abstract static class GetItemRootNode extends ObjectWrapperDescriptorRoot {
private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "i"), true, false);
@Child private ReadIndexedArgumentNode readArg1Node;
@Child private GetIndexNode getIndexNode;
GetItemRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ super(language, name, provider);
this.readArg1Node = ReadIndexedArgumentNode.create(1);
this.getIndexNode = GetIndexNode.create();
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.SSIZEARGFUNC, argConversions = {PythonToNativeNode.class, long.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long i);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
Object self = readSelf(frame);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(self);
Object arg1 = readArg1Node.execute(frame);
- return new Object[]{self, getIndexNode.execute(self, arg1)};
- }
-
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ensureReleaseNativeWrapperNode().execute(cArguments[0]);
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, getIndexNode.execute(self, arg1)));
}
@Override
@@ -1490,35 +1687,67 @@ public Signature getSignature() {
}
}
- /**
- * Implements semantics of {@code typeobject.c: wrap_sq_setitem}.
- */
- static final class SetItemRootNode extends MethodDescriptorRoot {
+ /** Implements semantics of {@code typeobject.c: wrap_sq_setitem}. */
+ @CApiWrapperDescriptor(value = SQ_SETITEM)
+ public abstract static class SetItemRootNode extends WrapperDescriptorRoot {
private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "i", "value"), true, false);
@Child private ReadIndexedArgumentNode readArg1Node;
@Child private ReadIndexedArgumentNode readArg2Node;
@Child private GetIndexNode getIndexNode;
SetItemRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ super(language, name, provider);
this.readArg1Node = ReadIndexedArgumentNode.create(1);
this.readArg2Node = ReadIndexedArgumentNode.create(2);
this.getIndexNode = GetIndexNode.create();
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.SSIZEOBJARGPROC, retConversion = int.class, argConversions = {PythonToNativeNode.class, long.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long i, Object value);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
Object self = readSelf(frame);
Object arg1 = readArg1Node.execute(frame);
- Object arg2 = readArg2Node.execute(frame);
- return new Object[]{self, getIndexNode.execute(self, arg1), arg2};
+ Object arg2 = ensurePythonObject(readArg2Node.execute(frame));
+
+ if (invokeExternalFunction(frame, boundFunction, self, (long) getIndexNode.execute(self, arg1), arg2) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
}
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[2]);
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
+ }
+
+ /** Implements semantics of {@code typeobject.c: wrap_sq_delitem}. */
+ @CApiWrapperDescriptor(value = SQ_DELITEM)
+ public abstract static class SqDelItemRootNode extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "key"), true, false);
+ @Child private ReadIndexedArgumentNode readKeyNode;
+ @Child private GetIndexNode getIndexNode;
+
+ SqDelItemRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ this.readKeyNode = ReadIndexedArgumentNode.create(1);
+ this.getIndexNode = GetIndexNode.create();
+ }
+
+ @InvokeExternalFunction(value = ExternalFunctionSignature.SSIZEOBJARGPROC, retConversion = int.class, argConversions = {PythonToNativeNode.class, long.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long key, Object value);
+
+ @Override
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object key = readKeyNode.execute(frame);
+
+ if (invokeExternalFunction(frame, boundFunction, self, getIndexNode.execute(self, key), PNone.NO_VALUE) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
}
@Override
@@ -1530,31 +1759,30 @@ public Signature getSignature() {
/**
* Implements semantics of {@code typeobject.c:wrap_descr_get}
*/
- public static final class DescrGetRootNode extends MethodDescriptorRoot {
+ @CApiWrapperDescriptor(value = DESCR_GET)
+ public abstract static class DescrGetRootNode extends ObjectWrapperDescriptorRoot {
private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "obj", "type"), true, false);
@Child private ReadIndexedArgumentNode readObj;
@Child private ReadIndexedArgumentNode readType;
public DescrGetRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ super(language, name, provider);
this.readObj = ReadIndexedArgumentNode.create(1);
this.readType = ReadIndexedArgumentNode.create(2);
}
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object self = readSelf(frame);
- Object obj = readObj.execute(frame);
- Object type = readType.execute(frame);
- return new Object[]{self, obj == PNone.NONE ? PNone.NO_VALUE : obj, type == PNone.NONE ? PNone.NO_VALUE : type};
- }
+ @InvokeExternalFunction(value = ExternalFunctionSignature.DESCRGETFUNC, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, PythonToNativeNode.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object obj, Object type);
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
- releaseNativeWrapperNode.execute(cArguments[2]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object obj = PythonUtils.normalizeNone(ConditionProfile.getUncached(), ensurePythonObject(readObj.execute(frame)));
+ Object type = PythonUtils.normalizeNone(ConditionProfile.getUncached(), ensurePythonObject(readType.execute(frame)));
+ if (obj == PNone.NO_VALUE && type == PNone.NO_VALUE) {
+ throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.TypeError, ErrorMessages.GET_NONE_NONE_IS_INVALID);
+ }
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, obj, type));
}
@Override
@@ -1563,30 +1791,29 @@ public Signature getSignature() {
}
}
- /**
- * Implements semantics of {@code typeobject.c:wrap_descr_delete}
- */
- public static final class DescrDeleteRootNode extends MethodDescriptorRoot {
+ /** Implements semantics of {@code typeobject.c: wrap_descr_delete} */
+ @CApiWrapperDescriptor(value = DESCR_DELETE)
+ public abstract static class DescrDeleteRootNode extends WrapperDescriptorRoot {
private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "obj"), true, false);
@Child private ReadIndexedArgumentNode readObj;
public DescrDeleteRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ super(language, name, provider);
this.readObj = ReadIndexedArgumentNode.create(1);
}
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object self = readSelf(frame);
- Object obj = readObj.execute(frame);
- return new Object[]{self, obj, PNone.NO_VALUE};
- }
+ @InvokeExternalFunction(value = ExternalFunctionSignature.DESCRSETFUNC, retConversion = int.class, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class,
+ PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object obj, Object value);
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object obj = ensurePythonObject(readObj.execute(frame));
+ if (invokeExternalFunction(frame, boundFunction, self, obj, PNone.NO_VALUE) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
}
@Override
@@ -1595,31 +1822,61 @@ public Signature getSignature() {
}
}
- /**
- * Implements semantics of {@code typeobject.c:wrap_delattr}
- */
- public static final class DelAttrRootNode extends MethodDescriptorRoot {
+ /** Implements semantics of {@code typeobject.c: wrap_delattr}. */
+ @CApiWrapperDescriptor(value = DELATTRO)
+ public abstract static class DelAttrRootNode extends WrapperDescriptorRoot {
private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "obj"), true, false);
@Child private ReadIndexedArgumentNode readObj;
public DelAttrRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ super(language, name, provider);
this.readObj = ReadIndexedArgumentNode.create(1);
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.SETATTROFUNC, retConversion = int.class, //
+ argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object obj, Object value);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
Object self = readSelf(frame);
- Object obj = readObj.execute(frame);
+ Object obj = ensurePythonObject(readObj.execute(frame));
// TODO: check if we need Carlo Verre hack here (see typeobject.c:hackcheck)
- return new Object[]{self, obj, PNone.NO_VALUE};
+ if (invokeExternalFunction(frame, boundFunction, self, obj, PNone.NO_VALUE) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
}
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
+ }
+
+ /** Implements semantics of {@code typeobject.c: wrap_delitem}. */
+ @CApiWrapperDescriptor(value = MP_DELITEM)
+ public abstract static class MpDelItemRootNode extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "key"), true, false);
+ @Child private ReadIndexedArgumentNode readKeyNode;
+
+ MpDelItemRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ this.readKeyNode = ReadIndexedArgumentNode.create(1);
+ }
+
+ @InvokeExternalFunction(value = ExternalFunctionSignature.OBJOBJARGPROC, retConversion = int.class, //
+ argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object key, Object value);
+
+ @Override
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object key = ensurePythonObject(readKeyNode.execute(frame));
+ if (invokeExternalFunction(frame, boundFunction, self, key, PNone.NO_VALUE) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
}
@Override
@@ -1629,31 +1886,50 @@ public Signature getSignature() {
}
/**
- * Implement mapping of {@code __delitem__} to {@code mp_ass_subscript}. It handles adding the
- * NULL 3rd argument.
+ * Implements semantics of {@code typeobject.c: wrap_ternaryfunc} and
+ * {@code typeobject.c: wrap_ternaryfunc_r}.
*/
- static final class MpDelItemRootNode extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "i"), true, false);
+ @CApiWrapperDescriptor(value = {TERNARYFUNC, TERNARYFUNC_R})
+ public abstract static class MethTernaryFuncRoot extends ObjectWrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "other", "third"), false, false);
+
@Child private ReadIndexedArgumentNode readArg1Node;
+ @Child private ReadIndexedArgumentNode readArg2Node;
- MpDelItemRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ private final boolean reverse;
+
+ MethTernaryFuncRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
this.readArg1Node = ReadIndexedArgumentNode.create(1);
+ this.readArg2Node = ReadIndexedArgumentNode.create(2);
+ this.reverse = provider == TERNARYFUNC_R;
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.TERNARYFUNC, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, PythonToNativeNode.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object other, Object third);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
Object self = readSelf(frame);
- Object arg1 = readArg1Node.execute(frame);
- return new Object[]{self, arg1, PNone.NO_VALUE};
- }
+ Object other = ensurePythonObject(readArg1Node.execute(frame));
+ Object third = ensurePythonObject(readArg2Node.execute(frame));
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
- releaseNativeWrapperNode.execute(cArguments[2]);
+ // normalize NO_VALUE to NONE
+ if (third == PNone.NO_VALUE) {
+ third = PNone.NONE;
+ }
+
+ // flip 'self' and 'other' in case of reverse operation
+ Object first, second;
+ if (reverse) {
+ first = other;
+ second = self;
+ } else {
+ first = self;
+ second = other;
+ }
+
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, first, second, third));
}
@Override
@@ -1662,75 +1938,110 @@ public Signature getSignature() {
}
}
- /**
- * Wrapper root node for reverse binary operations.
- */
- static final class MethReverseRootNode extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "obj"), true, false);
- @Child private ReadIndexedArgumentNode readArg0Node;
- @Child private ReadIndexedArgumentNode readArg1Node;
+ /** Implements semantics of {@code typeobject.c: wrap_richcmpfunc} */
+ @CApiWrapperDescriptor(value = {GT, GE, LE, LT, EQ, NE})
+ public abstract static class MethRichcmpOpRootNode extends ObjectWrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "other"), true, false);
- MethReverseRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
- this.readArg0Node = ReadIndexedArgumentNode.create(0);
- this.readArg1Node = ReadIndexedArgumentNode.create(1);
+ @Child private ReadIndexedArgumentNode readArgNode;
+
+ private final int op;
+
+ MethRichcmpOpRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ this.readArgNode = ReadIndexedArgumentNode.create(1);
+ this.op = getCompareOpCode(provider);
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.RICHCMPFUNC, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, int.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object other, int op);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object arg0 = readArg0Node.execute(frame);
- Object arg1 = readArg1Node.execute(frame);
- return new Object[]{arg1, arg0};
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object other = ensurePythonObject(readArgNode.execute(frame));
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, other, op));
+ }
+
+ @Override
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
+
+ private static int getCompareOpCode(PExternalFunctionWrapper sig) {
+ // op codes for binary comparisons (defined in 'object.h')
+ return switch (sig) {
+ case LT -> RichCmpOp.Py_LT.asNative();
+ case LE -> RichCmpOp.Py_LE.asNative();
+ case EQ -> RichCmpOp.Py_EQ.asNative();
+ case NE -> RichCmpOp.Py_NE.asNative();
+ case GT -> RichCmpOp.Py_GT.asNative();
+ case GE -> RichCmpOp.Py_GE.asNative();
+ default -> throw CompilerDirectives.shouldNotReachHere(sig.getName());
+ };
+ }
+ }
+
+ /** Implements semantics of {@code typeobject.c: wrap_next}. */
+ @CApiWrapperDescriptor(value = ITERNEXT)
+ public abstract static class IterNextFuncRootNode extends ObjectWrapperDescriptorRoot {
+
+ @Child private CheckIterNextResultNode checkIterNextResultNode;
+
+ IterNextFuncRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
+ this.checkIterNextResultNode = CheckIterNextResultNodeGen.create();
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.UNARYFUNC, argConversions = {PythonToNativeNode.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self);
+
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ long lresult = invokeExternalFunction(frame, boundFunction, self);
+ PythonContext context = PythonContext.get(this);
+ return checkIterNextResultNode.execute(context.getThreadState(context.getLanguage()), ensureNativeToPythonReturnNode().executeRaw(lresult));
}
@Override
public Signature getSignature() {
- return SIGNATURE;
+ // same signature as a method without arguments (just the self)
+ return MethNoargsRoot.SIGNATURE;
}
}
/**
- * Wrapper root node for native power function (with an optional third argument).
+ * Wrapper root node for C function type {@code getter}.
*/
- static class MethPowRootNode extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, 0, tsArray("args"), false, false);
-
- @Child private ReadVarArgsNode readVarargsNode;
+ @CApiWrapperDescriptor(value = GETTER)
+ public abstract static class GetterRoot extends ObjectWrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignatureWithClosure(false, -1, tsArray("self"), true, false);
- private final ConditionProfile profile;
+ @Child private ReadIndexedArgumentNode readClosureNode;
- MethPowRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
- this.readVarargsNode = ReadVarArgsNode.create(SIGNATURE.varArgsPArgumentsIndex());
- this.profile = ConditionProfile.create();
+ public GetterRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.GETTER, argConversions = {PythonToNativeNode.class, long.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, long closure);
+
@Override
- protected final Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
Object self = readSelf(frame);
- Object[] varargs = readVarargsNode.execute(frame);
- Object arg0 = varargs[0];
- Object arg1 = profile.profile(varargs.length > 1) ? varargs[1] : PNone.NONE;
- return getArguments(self, arg0, arg1);
- }
-
- Object[] getArguments(Object arg0, Object arg1, Object arg2) {
- return new Object[]{arg0, arg1, arg2};
+ if (readClosureNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ readClosureNode = insert(createReadClosureNode(SIGNATURE));
+ }
+ long closure = (long) readClosureNode.execute(frame);
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, self, closure));
}
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
- releaseNativeWrapperNode.execute(cArguments[2]);
+ static ReadIndexedArgumentNode createReadClosureNode(Signature signature) {
+ // we insert a hidden argument after the hidden callable arg
+ int hiddenArg = signature.getParameterIds().length + 1;
+ return ReadIndexedArgumentNode.create(hiddenArg);
}
@Override
@@ -1740,47 +2051,71 @@ public Signature getSignature() {
}
/**
- * Wrapper root node for native reverse power function (with an optional third argument).
+ * Wrapper root node for C function type {@code setter}.
*/
- static final class MethRPowRootNode extends MethPowRootNode {
+ @CApiWrapperDescriptor(value = SETTER)
+ public abstract static class SetterRoot extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignatureWithClosure(false, -1, tsArray("self", "value"), true, false);
+
+ @Child private ReadIndexedArgumentNode readClosureNode;
+ @Child private ReadIndexedArgumentNode readArgNode;
- MethRPowRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ public SetterRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
super(language, name, provider);
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.SETTER, retConversion = int.class, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, long.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object value, long closure);
+
@Override
- Object[] getArguments(Object arg0, Object arg1, Object arg2) {
- return new Object[]{arg1, arg0, arg2};
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object arg = ensurePythonObject(ensureReadArgNode().execute(frame));
+ if (readClosureNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ readClosureNode = insert(GetterRoot.createReadClosureNode(SIGNATURE));
+ }
+ long closure = (long) readClosureNode.execute(frame);
+ if (invokeExternalFunction(frame, boundFunction, self, arg, closure) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
}
- }
-
- /**
- * Wrapper root node for native power function (with an optional third argument).
- */
- static final class MethRichcmpOpRootNode extends MethodDescriptorRoot {
- private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "other"), true, false);
- @Child private ReadIndexedArgumentNode readArgNode;
- private final int op;
+ @Override
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
- MethRichcmpOpRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider, int op) {
- super(language, name, false, provider);
- this.readArgNode = ReadIndexedArgumentNode.create(1);
- this.op = op;
+ private ReadIndexedArgumentNode ensureReadArgNode() {
+ if (readArgNode == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ readArgNode = insert(ReadIndexedArgumentNode.create(1));
+ }
+ return readArgNode;
}
+ }
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object self = readSelf(frame);
- Object arg = readArgNode.execute(frame);
- return new Object[]{self, arg, op};
+ /** Implements semantics of {@code typeobject.c: wrap_lenfunc} */
+ @CApiWrapperDescriptor(value = {LENFUNC, HASHFUNC})
+ public abstract static class MethLenfuncRoot extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self"), true, false);
+
+ public MethLenfuncRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ super(language, name, provider);
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.LENFUNC, argConversions = {PythonToNativeNode.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self);
+
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ long result = invokeExternalFunction(frame, boundFunction, self);
+ if (result == -1) {
+ transformExceptionFromNative(false);
+ }
+ return result;
}
@Override
@@ -1789,71 +2124,112 @@ public Signature getSignature() {
}
}
- /**
- * Wrapper root node for C function type {@code iternextfunc}.
- */
- static class IterNextFuncRootNode extends MethodDescriptorRoot {
+ /** Implements semantics of {@code typeobject.c: wrap_objobjproc}. */
+ @CApiWrapperDescriptor(value = OBJOBJPROC)
+ public abstract static class MethObjObjProcRoot extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "value"), true, false);
- IterNextFuncRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
- }
+ @Child private ReadIndexedArgumentNode readValueNode;
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- return new Object[]{readSelf(frame)};
+ MethObjObjProcRoot(PythonLanguage lang, TruffleString name, PExternalFunctionWrapper provider) {
+ super(lang, name, provider);
+ this.readValueNode = ReadIndexedArgumentNode.create(1);
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.OBJOBJPROC, retConversion = int.class, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object value);
+
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ensureReleaseNativeWrapperNode().execute(cArguments[0]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object value = ensurePythonObject(readValueNode.execute(frame));
+
+ int result = invokeExternalFunction(frame, boundFunction, self, value);
+ if (result == -1) {
+ transformExceptionFromNative(false);
+ }
+ return result != 0;
}
@Override
public Signature getSignature() {
- // same signature as a method without arguments (just the self)
- return MethNoargsRoot.SIGNATURE;
+ return SIGNATURE;
}
}
- abstract static class GetSetRootNode extends MethodDescriptorRoot {
+ /** Implements semantics of {@code typeobject.c: wrap_objobjargproc}. */
+ @CApiWrapperDescriptor(value = OBJOBJARGPROC)
+ public abstract static class MethObjObjArgProcRoot extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "key", "value"), true, false);
- @Child private ReadIndexedArgumentNode readClosureNode;
+ @Child private ReadIndexedArgumentNode readKeyNode;
+ @Child private ReadIndexedArgumentNode readValueNode;
- GetSetRootNode(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, false, provider);
+ MethObjObjArgProcRoot(PythonLanguage lang, TruffleString name, PExternalFunctionWrapper provider) {
+ super(lang, name, provider);
+ this.readKeyNode = ReadIndexedArgumentNode.create(1);
+ this.readValueNode = ReadIndexedArgumentNode.create(2);
}
- protected final Object readClosure(VirtualFrame frame) {
- if (readClosureNode == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- // we insert a hidden argument after the hidden callable arg
- int hiddenArg = getSignature().getParameterIds().length + 1;
- readClosureNode = insert(ReadIndexedArgumentNode.create(hiddenArg));
+ @InvokeExternalFunction(value = ExternalFunctionSignature.OBJOBJARGPROC, retConversion = int.class, //
+ argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object key, Object value);
+
+ @Override
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = readSelf(frame);
+ Object key = ensurePythonObject(readKeyNode.execute(frame));
+ Object value = ensurePythonObject(readValueNode.execute(frame));
+
+ if (invokeExternalFunction(frame, boundFunction, self, key, value) == -1) {
+ transformExceptionFromNative(false);
}
- return readClosureNode.execute(frame);
+ return PNone.NONE;
}
+ @Override
+ public Signature getSignature() {
+ return SIGNATURE;
+ }
}
/**
- * Wrapper root node for C function type {@code getter}.
+ * Implements semantics of {@code typeobject.c: wrap_binaryfunc} and
+ * {@code typeobject.c: wrap_binaryfunc_r}.
*/
- public static class GetterRoot extends GetSetRootNode {
- private static final Signature SIGNATURE = createSignatureWithClosure(false, -1, tsArray("self"), true, false);
+ @CApiWrapperDescriptor(value = {BINARYFUNC, BINARYFUNC_L, BINARYFUNC_R})
+ public abstract static class MethBinaryRoot extends ObjectWrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "other"), true, false);
- public GetterRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
- super(language, name, provider);
+ @Child private ReadIndexedArgumentNode readOtherNode;
+
+ private final boolean reverse;
+
+ MethBinaryRoot(PythonLanguage lang, TruffleString name, PExternalFunctionWrapper provider) {
+ super(lang, name, provider);
+ this.readOtherNode = ReadIndexedArgumentNode.create(1);
+ this.reverse = provider == BINARYFUNC_R;
}
+ @InvokeExternalFunction(value = ExternalFunctionSignature.BINARYFUNC, argConversions = {PythonToNativeNode.class, PythonToNativeNode.class})
+ protected abstract long invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object other);
+
@Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
Object self = readSelf(frame);
- return new Object[]{self, readClosure(frame)};
- }
+ Object other = ensurePythonObject(readOtherNode.execute(frame));
- @Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ensureReleaseNativeWrapperNode().execute(cArguments[0]);
+ // flip arguments 'self' and 'other' in case of reverse operation
+ Object arg0, arg1;
+ if (reverse) {
+ arg0 = other;
+ arg1 = self;
+ } else {
+ arg0 = self;
+ arg1 = other;
+ }
+
+ return returnNativeObjectToPython(invokeExternalFunction(frame, boundFunction, arg0, arg1));
}
@Override
@@ -1862,52 +2238,45 @@ public Signature getSignature() {
}
}
- /**
- * Wrapper root node for C function type {@code setter}.
- */
- public static class SetterRoot extends GetSetRootNode {
- private static final Signature SIGNATURE = createSignatureWithClosure(false, -1, tsArray("self", "value"), true, false);
+ /** Implements semantics of {@code typeobject.c: wrap_descr_set} */
+ @CApiWrapperDescriptor(value = DESCR_SET)
+ public abstract static class MethDescrSetRoot extends WrapperDescriptorRoot {
+ private static final Signature SIGNATURE = createSignature(false, -1, tsArray("self", "instance", "value"), true, false);
+ @Child private ReadIndexedArgumentNode readInstanceNode;
+ @Child private ReadIndexedArgumentNode readValueNode;
- @Child private ReadIndexedArgumentNode readArgNode;
-
- public SetterRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
+ MethDescrSetRoot(PythonLanguage language, TruffleString name, PExternalFunctionWrapper provider) {
super(language, name, provider);
+ this.readInstanceNode = ReadIndexedArgumentNode.create(1);
+ this.readValueNode = ReadIndexedArgumentNode.create(2);
}
- @Override
- protected Object[] prepareCArguments(VirtualFrame frame) {
- Object self = readSelf(frame);
- Object arg = ensureReadArgNode().execute(frame);
- return new Object[]{self, arg, readClosure(frame)};
- }
+ @InvokeExternalFunction(value = ExternalFunctionSignature.DESCRSETFUNC, retConversion = int.class, //
+ argConversions = {PythonToNativeNode.class, PythonToNativeNode.class, PythonToNativeNode.class})
+ protected abstract int invokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction, Object self, Object instance, Object value);
@Override
- protected void postprocessCArguments(VirtualFrame frame, Object[] cArguments) {
- ReleaseNativeWrapperNode releaseNativeWrapperNode = ensureReleaseNativeWrapperNode();
- releaseNativeWrapperNode.execute(cArguments[0]);
- releaseNativeWrapperNode.execute(cArguments[1]);
+ protected Object readArgumentsAndInvokeExternalFunction(VirtualFrame frame, NativeFunctionPointer boundFunction) {
+ Object self = ensurePythonObject(readSelf(frame));
+ Object instance = ensurePythonObject(readInstanceNode.execute(frame));
+ Object value = ensurePythonObject(readValueNode.execute(frame));
+ if (invokeExternalFunction(frame, boundFunction, self, instance, value) < 0) {
+ transformExceptionFromNative();
+ }
+ return PNone.NONE;
}
@Override
public Signature getSignature() {
return SIGNATURE;
}
-
- private ReadIndexedArgumentNode ensureReadArgNode() {
- if (readArgNode == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- readArgNode = insert(ReadIndexedArgumentNode.create(1));
- }
- return readArgNode;
- }
}
/**
* An inlined node-like object for keeping track of eager native allocation state bit. Should be
- * {@code @Cached} and passed into
- * {@link CreateArgsTupleNode#execute(Node, PythonLanguage, Object[], EagerTupleState)}. Then
- * the {@link #report(Node, PTuple)} method should be called with the tuple after the native
- * call returns.
+ * {@code @Cached} and passed into {@link CreateNativeArgsTupleNode#execute}. Then the
+ * {@link #report(Node, PTuple)} method should be called with the tuple after the native call
+ * returns.
*/
public static final class EagerTupleState {
private final StateField state;
@@ -1949,148 +2318,113 @@ public static EagerTupleState getUncached() {
}
/**
- * We need to inflate all primitives in order to avoid memory leaks. Explanation: Primitives
- * would currently be wrapped into a PrimitiveNativeWrapper. If any of those will receive a
- * toNative message, the managed code will be the only owner of those wrappers. But we will
- * never be able to reach the wrapper from the arguments if they are just primitive. So, we
- * inflate the primitives and we can then traverse the tuple and reach the wrappers of its
- * arguments after the call returned.
+ * Allocates a native tuple and initializes it with the given elements. The elements will be
+ * promoted to Python objects using {@link EnsurePythonObjectNode} (not promoting boxable
+ * primitives) and written into the passed array.
+ *
+ * For performance reasons, this node takes some shortcuts: It allocates the native tuple using
+ * {@link NativeCAPISymbol#FUN_PY_TYPE_GENERIC_ALLOC PyType_GenericAlloc} and initializes the
+ * elements by writing them directly to {@link CFields#PyTupleObject__ob_item}.
+ *
+ * Also, this node will not register the tuple to the
+ * {@link com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandleContext#nativeLookup
+ * nativeLookup} and thus won't create a {@link PythonAbstractNativeObject native wrapper} for
+ * it. In this way, the {@link ReleaseNativeArgsTupleNode release node} can detect if the tuple
+ * escaped to managed.
*/
@GenerateInline(false)
@GenerateUncached
- public abstract static class CreateArgsTupleNode extends Node {
- public abstract PTuple execute(PythonLanguage language, Object[] args, boolean eagerNative);
-
- public final PTuple execute(Node inliningTarget, PythonLanguage language, Object[] args, EagerTupleState state) {
- return execute(language, args, state.isEager(inliningTarget));
- }
+ public abstract static class CreateNativeArgsTupleNode extends Node {
+ private static final CApiTiming TIMING_invokeTypeGenericAlloc = CApiTiming.create(true, "PyType_GenericAlloc");
- @Specialization(guards = {"args.length == cachedLen", "cachedLen <= 8", "!eagerNative"}, limit = "1")
- @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL)
- static PTuple doCachedLen(PythonLanguage language, Object[] args, @SuppressWarnings("unused") boolean eagerNative,
- @Cached("args.length") int cachedLen,
- @Cached("createMaterializeNodes(args.length)") MaterializePrimitiveNode[] materializePrimitiveNodes) {
+ static final TruffleLogger LOGGER = CApiContext.getLogger(CreateNativeArgsTupleNode.class);
- for (int i = 0; i < cachedLen; i++) {
- args[i] = materializePrimitiveNodes[i].execute(language, args[i]);
- }
- return PFactory.createTuple(language, args);
- }
+ public abstract long execute(PythonContext context, Object[] args);
- @Specialization(guards = {"args.length == cachedLen", "cachedLen <= 8", "eagerNative"}, limit = "1", replaces = "doCachedLen")
- @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL)
- static PTuple doCachedLenEagerNative(PythonLanguage language, Object[] args, @SuppressWarnings("unused") boolean eagerNative,
+ @Specialization
+ static long doGeneric(PythonContext context, Object[] args,
@Bind Node inliningTarget,
- @Cached("args.length") int cachedLen,
- @Cached("createMaterializeNodes(args.length)") MaterializePrimitiveNode[] materializePrimitiveNodes,
- @Exclusive @Cached StorageToNativeNode storageToNativeNode) {
-
- for (int i = 0; i < cachedLen; i++) {
- args[i] = materializePrimitiveNodes[i].execute(language, args[i]);
+ @Cached EnsurePythonObjectNode materializePrimitiveNode,
+ @Cached PythonToNativeInternalNode pythonToNativeNode) {
+ if (!context.isNativeAccessAllowed()) {
+ throw CompilerDirectives.shouldNotReachHere();
}
- return PFactory.createTuple(language, storageToNativeNode.execute(inliningTarget, args, cachedLen, true));
- }
-
- @Specialization(replaces = {"doCachedLen", "doCachedLenEagerNative"})
- static PTuple doGeneric(PythonLanguage language, Object[] args, boolean eagerNative,
- @Bind Node inliningTarget,
- @Cached MaterializePrimitiveNode materializePrimitiveNode,
- @Exclusive @Cached StorageToNativeNode storageToNativeNode) {
int n = args.length;
- for (int i = 0; i < n; i++) {
- args[i] = materializePrimitiveNode.execute(language, args[i]);
- }
- SequenceStorage storage;
- if (eagerNative) {
- storage = storageToNativeNode.execute(inliningTarget, args, n, true);
- } else {
- storage = new ObjectSequenceStorage(args);
- }
- return PFactory.createTuple(language, storage);
- }
- static MaterializePrimitiveNode[] createMaterializeNodes(int length) {
- MaterializePrimitiveNode[] materializePrimitiveNodes = new MaterializePrimitiveNode[length];
- for (int i = 0; i < length; i++) {
- materializePrimitiveNodes[i] = MaterializePrimitiveNodeGen.create();
- }
- return materializePrimitiveNodes;
- }
- }
+ assert (GetTypeFlagsNode.executeUncached(PythonBuiltinClassType.PTuple) & TypeFlags.HAVE_GC) != 0;
- @GenerateInline(false)
- @ImportStatic(PythonUtils.class)
- abstract static class ReleaseNativeSequenceStorageNode extends Node {
+ PythonBuiltinClass argsTupleClass = context.lookupType(PythonBuiltinClassType.PTuple);
+ NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, FUN_PY_TYPE_GENERIC_ALLOC);
+ long op = ExternalFunctionInvoker.invokeTYPE_GENERIC_ALLOC(null, TIMING_invokeTypeGenericAlloc, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(), context.getThreadState(context.getLanguage(inliningTarget)), callable,
+ pythonToNativeNode.execute(inliningTarget, argsTupleClass, false), n);
- abstract void execute(NativeSequenceStorage storage);
+ long obItem = CStructAccess.getFieldPtr(op, CFields.PyTupleObject__ob_item);
- @Specialization(guards = {"storage.length() == cachedLen", "cachedLen <= 8"}, limit = "1")
- @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL)
- static void doObjectCachedLen(NativeObjectSequenceStorage storage,
- @Bind Node inliningTarget,
- @Cached("storage.length()") int cachedLen,
- @Shared @Cached CStructAccess.ReadPointerNode readNode,
- @Shared @Cached CExtNodes.XDecRefPointerNode decRefPointerNode,
- @Shared @Cached CStructAccess.FreeNode freeNode) {
- for (int i = 0; i < cachedLen; i++) {
- Object elementPointer = readNode.readArrayElement(storage.getPtr(), i);
- decRefPointerNode.execute(inliningTarget, elementPointer);
+ for (int i = 0; i < n; i++) {
+ Object promoted = materializePrimitiveNode.execute(context, args[i], false);
+ args[i] = promoted;
+ writePtrArrayElement(obItem, i, pythonToNativeNode.execute(inliningTarget, promoted, true));
}
- // in this case, the runtime still exclusively owns the memory
- freeNode.free(storage.getPtr());
- }
- @Specialization(replaces = "doObjectCachedLen")
- static void doObjectGeneric(NativeObjectSequenceStorage storage,
- @Bind Node inliningTarget,
- @Shared @Cached CStructAccess.ReadPointerNode readNode,
- @Shared @Cached CExtNodes.XDecRefPointerNode decRefPointerNode,
- @Shared @Cached CStructAccess.FreeNode freeNode) {
- for (int i = 0; i < storage.length(); i++) {
- Object elementPointer = readNode.readArrayElement(storage.getPtr(), i);
- decRefPointerNode.execute(inliningTarget, elementPointer);
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Created native args tuple %x (size=%d)", op, n));
}
- // in this case, the runtime still exclusively owns the memory
- freeNode.free(storage.getPtr());
+
+ assert !HandlePointerConverter.pointsToPyHandleSpace(op);
+ assert PyObjectGCTrackNode.isGcTracked(op);
+ return op;
}
}
/**
- * Special helper nodes that materializes any primitive that would leak the wrapper if the
- * reference is owned by managed code only.
+ * Attempts to eagerly release the native tuple previously created with
+ * {@link CreateNativeArgsTupleNode} if it did not escape.
*/
@GenerateInline(false)
@GenerateUncached
- abstract static class MaterializePrimitiveNode extends Node {
-
- public abstract Object execute(PythonLanguage language, Object object);
-
- // NOTE: Booleans don't need to be materialized because they are singletons.
+ public abstract static class ReleaseNativeArgsTupleNode extends Node {
+ private static final CApiTiming TIMING_invokePyDealloc = CApiTiming.create(true, "_Py_Dealloc");
- @Specialization
- static PInt doInteger(PythonLanguage language, int i) {
- return PFactory.createInt(language, i);
- }
-
- @Specialization
- static PInt doLong(PythonLanguage language, long l) {
- return PFactory.createInt(language, l);
- }
-
- @Specialization
- static PFloat doDouble(PythonLanguage language, double d) {
- return PFactory.createFloat(language, d);
- }
+ public abstract void execute(long argsTuplePtr, Object[] managedArgs);
@Specialization
- static PString doString(PythonLanguage language, TruffleString s) {
- return PFactory.createString(language, s);
- }
-
- @Fallback
- static Object doObject(@SuppressWarnings("unused") PythonLanguage language, Object object) {
- return object;
+ static void doGeneric(long argsTuplePtr, Object[] managedArgs,
+ @Bind Node inliningTarget) {
+ assert !HandlePointerConverter.pointsToPyHandleSpace(argsTuplePtr);
+ assert IsSameTypeNode.executeUncached(GetNativeClassNodeGen.getUncached().execute(null, new PythonAbstractNativeObject(argsTuplePtr)), PythonBuiltinClassType.PTuple);
+ long refCount = CApiTransitions.readNativeRefCount(argsTuplePtr);
+ assert refCount >= 1;
+ if (refCount > 1) {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Native args tuple %x escaped (refcount = %d). Decref'ing by 1.", argsTuplePtr, refCount));
+ }
+ // The args tuple escaped. We cannot do anything and just give away our reference.
+ CApiTransitions.subNativeRefCount(argsTuplePtr, 1);
+ return;
+ }
+ assert readLongField(argsTuplePtr, CFields.PyVarObject__ob_size) == managedArgs.length;
+ assert verifyElements(argsTuplePtr, managedArgs);
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Releasing native args tuple %x.", argsTuplePtr));
+ }
+ CApiTransitions.subNativeRefCount(argsTuplePtr, 1);
+ PythonContext context = PythonContext.get(inliningTarget);
+ NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, FUN_PY_DEALLOC);
+ ExternalFunctionInvoker.invokePY_DEALLOC(null, TIMING_invokePyDealloc, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(), context.getThreadState(context.getLanguage(inliningTarget)), callable,
+ argsTuplePtr);
+ }
+
+ private static boolean verifyElements(long op, Object[] managedArgs) {
+ long obItem = CStructAccess.getFieldPtr(op, CFields.PyTupleObject__ob_item);
+ for (int i = 0; i < managedArgs.length; i++) {
+ if (readPtrArrayElement(obItem, i) != PythonToNativeInternalNode.executeUncached(managedArgs[i], false)) {
+ return false;
+ }
+ }
+ return true;
}
}
@@ -2098,93 +2432,20 @@ static Object doObject(@SuppressWarnings("unused") PythonLanguage language, Obje
@ImportStatic(PGuards.class)
@GenerateUncached
@GenerateInline(false)
- public abstract static class DefaultCheckFunctionResultNode extends CheckFunctionResultNode {
-
- @Specialization
- static Object doNativeWrapper(PythonThreadState state, TruffleString name, @SuppressWarnings("unused") PythonNativeWrapper result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, false, true);
- return result;
- }
-
- @Specialization(guards = "isNoValue(result)")
- static Object doNoValue(PythonThreadState state, TruffleString name, @SuppressWarnings("unused") PNone result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, true, true);
- return PNone.NO_VALUE;
- }
-
- @Specialization(guards = "!isNoValue(result)")
- static Object doPythonObject(PythonThreadState state, TruffleString name, @SuppressWarnings("unused") PythonAbstractObject result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, false, true);
- return result;
- }
-
- @Specialization
- static Object doNativePointer(PythonThreadState state, TruffleString name, NativePointer result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, result.isNull(), true);
- return result;
- }
-
- @Specialization
- static int doInteger(PythonThreadState state, TruffleString name, int result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- /*
- * If the native functions returns a primitive int, only a value '-1' indicates an
- * error. However, '-1' may also be a valid return value. So, don't be strict.
- */
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, result == -1, false);
- return result;
- }
-
- @Specialization
- static long doLong(PythonThreadState state, TruffleString name, long result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- /*
- * If the native functions returns a primitive long, only a value '-1' indicates an
- * error. However, '-1' may also be a valid return value. So, don't be strict.
- */
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, result == -1, false);
- return result;
- }
+ public abstract static class PyObjectCheckFunctionResultNode extends Node {
- /*
- * Our fallback case, but with some cached params. PythonNativeWrapper results should be
- * unwrapped and recursively delegated (see #doNativeWrapper) and PNone is treated
- * specially, because we consider it as null in #doNoValue and as not null in
- * #doPythonObject
- */
- @Specialization(guards = {"!isPythonNativeWrapper(result)", "!isPNone(result)"})
- static Object doForeign(PythonThreadState state, TruffleString name, Object result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode,
- @Exclusive @CachedLibrary(limit = "3") InteropLibrary lib) {
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, lib.isNull(result), true);
- return result;
+ public final Object execute(PythonContext context, TruffleString name, Object result) {
+ PythonLanguage language = context.getLanguage(this);
+ return execute(context.getThreadState(language), name, result);
}
- protected static boolean isPythonNativeWrapper(Object object) {
- return object instanceof PythonNativeWrapper;
- }
+ public abstract Object execute(PythonThreadState threadState, TruffleString name, Object result);
- public static DefaultCheckFunctionResultNode getUncached() {
- return DefaultCheckFunctionResultNodeGen.getUncached();
+ @TruffleBoundary
+ public static Object executeUncached(TruffleString name, Object result) {
+ return PyObjectCheckFunctionResultNodeGen.getUncached().execute(PythonContext.get(null), name, result);
}
- }
- // roughly equivalent to _Py_CheckFunctionResult in Objects/call.c
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class PyObjectCheckFunctionResultNode extends CheckFunctionResultNode {
@Specialization(guards = "!isForeignObject.execute(inliningTarget, result)")
static Object doPythonObject(PythonThreadState state, TruffleString name, Object result,
@Bind Node inliningTarget,
@@ -2215,16 +2476,16 @@ static Object doForeign(PythonThreadState state, TruffleString name, Object resu
* Equivalent of the result processing part in {@code Objects/typeobject.c: wrap_next}.
*/
@GenerateInline(false)
- @GenerateUncached
- public abstract static class CheckIterNextResultNode extends CheckFunctionResultNode {
+ abstract static class CheckIterNextResultNode extends Node {
+
+ abstract Object execute(PythonThreadState state, Object result);
- @Specialization(limit = "3")
- static Object doGeneric(PythonThreadState state, @SuppressWarnings("unused") TruffleString name, Object result,
+ @Specialization
+ static Object doGeneric(PythonThreadState state, Object result,
@Bind Node inliningTarget,
- @CachedLibrary("result") InteropLibrary lib,
- @Cached CExtCommonNodes.ReadAndClearNativeException readAndClearNativeException,
+ @Cached ReadAndClearNativeException readAndClearNativeException,
@Cached PRaiseNode raiseNode) {
- if (lib.isNull(result)) {
+ if (result == PNone.NO_VALUE) {
Object currentException = readAndClearNativeException.execute(inliningTarget, state);
// if no exception occurred, the iterator is exhausted -> raise StopIteration
if (currentException == PNone.NO_VALUE) {
@@ -2237,54 +2498,6 @@ static Object doGeneric(PythonThreadState state, @SuppressWarnings("unused") Tru
}
}
- /**
- * Processes the function result with CPython semantics:
- *
- *
- *
- * This is the case for {@code wrap_init}, {@code wrap_descr_delete}, {@code wrap_descr_set},
- * {@code wrap_delattr}, {@code wrap_setattr}.
- */
- @ImportStatic(PGuards.class)
- @GenerateInline(false)
- @GenerateUncached
- public abstract static class InitCheckFunctionResultNode extends CheckFunctionResultNode {
- @Specialization
- @SuppressWarnings("unused")
- static Object doInt(PythonThreadState state, TruffleString name, int result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, result < 0, true);
- return PNone.NONE;
- }
-
- // Slow path
- @Specialization(replaces = "doInt")
- @InliningCutoff
- static Object notNumber(PythonThreadState state, @SuppressWarnings("unused") TruffleString name, Object result,
- @Bind Node inliningTarget,
- @CachedLibrary(limit = "2") InteropLibrary lib,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- int ret = 0;
- if (lib.isNumber(result)) {
- try {
- ret = lib.asInt(result);
- if (ret >= 0) {
- return PNone.NONE;
- }
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
- transformExceptionFromNativeNode.execute(inliningTarget, state, name, ret < 0, true);
- return result;
- }
- }
-
/**
* Processes the function result with CPython semantics:
*
@@ -2297,78 +2510,18 @@ static Object notNumber(PythonThreadState state, @SuppressWarnings("unused") Tru
* This is the case for {@code wrap_delitem}, {@code wrap_objobjargproc},
* {@code wrap_sq_delitem}, {@code wrap_sq_setitem}, {@code asdf}.
*/
- @ImportStatic(PGuards.class)
@GenerateUncached
- @GenerateInline(false)
- public abstract static class CheckPrimitiveFunctionResultNode extends CheckFunctionResultNode {
- public abstract long executeLong(PythonThreadState threadState, TruffleString name, Object result);
+ @GenerateInline
+ @GenerateCached(false)
+ public abstract static class CheckPrimitiveFunctionResultNode extends Node {
+ public abstract long executeLong(Node inliningTarget, PythonThreadState threadState, TruffleString name, long result);
@Specialization
- static long doLong(PythonThreadState threadState, TruffleString name, long result,
- @Bind Node inliningTarget,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
+ static long doLong(Node inliningTarget, PythonThreadState threadState, TruffleString name, long result,
+ @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
transformExceptionFromNativeNode.execute(inliningTarget, threadState, name, result == -1, false);
return result;
}
-
- @Specialization(replaces = "doLong")
- @InliningCutoff
- static long doGeneric(PythonThreadState threadState, TruffleString name, Object result,
- @Bind Node inliningTarget,
- @CachedLibrary(limit = "2") InteropLibrary lib,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- if (lib.fitsInLong(result)) {
- try {
- long ret = lib.asLong(result);
- transformExceptionFromNativeNode.execute(inliningTarget, threadState, name, ret == -1, false);
- return ret;
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
- throw CompilerDirectives.shouldNotReachHere("expected primitive function result but does not fit into Java long");
- }
}
- /**
- * Tests if the primitive result of the called function is {@code -1} and if an error occurred.
- * In this case, the error is re-raised. Otherwise, it converts the result to a Boolean. This is
- * equivalent to the result processing part in {@code Object/typeobject.c: wrap_inquirypred} and
- * {@code Object/typeobject.c: wrap_objobjproc}.
- */
- @GenerateInline(false)
- @GenerateUncached
- public abstract static class CheckInquiryResultNode extends CheckFunctionResultNode {
-
- public abstract boolean executeBool(PythonThreadState threadState, TruffleString name, Object result);
-
- @Specialization
- static boolean doLong(PythonThreadState threadState, TruffleString name, long result,
- @Bind Node inliningTarget,
- @Shared @Cached InlinedConditionProfile resultProfile,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- transformExceptionFromNativeNode.execute(inliningTarget, threadState, name, result == -1, false);
- return resultProfile.profile(inliningTarget, result != 0);
- }
-
- @Specialization(replaces = "doLong")
- @InliningCutoff
- static boolean doGeneric(PythonThreadState threadState, TruffleString name, Object result,
- @Bind Node inliningTarget,
- @Shared @Cached InlinedConditionProfile resultProfile,
- @CachedLibrary(limit = "3") InteropLibrary lib,
- @Shared @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
- if (lib.fitsInLong(result)) {
- try {
- long lresult = lib.asLong(result);
- transformExceptionFromNativeNode.execute(inliningTarget, threadState, name, lresult == -1, false);
- return resultProfile.profile(inliningTarget, lresult != 0);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- }
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.FUNC_DIDNT_RETURN_INT, name);
- }
- }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionSignature.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionSignature.java
new file mode 100644
index 0000000000..6641a2c84f
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ExternalFunctionSignature.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2026, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package com.oracle.graal.python.builtins.objects.cext.capi;
+
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.CharPtrAsTruffleString;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Double;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT64_T;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Pointer;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PrimitiveResult64;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectReturn;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyThreadState;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObject;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.SIZE_T;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UINTPTR_T;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void;
+import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
+
+import com.oracle.graal.python.annotations.CApiExternalFunctionSignatures;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
+import com.oracle.graal.python.builtins.objects.cext.common.NativeCExtSymbol;
+import com.oracle.truffle.api.strings.TruffleString;
+
+/**
+ * Enum of well-known function and slot signatures. The integer values must stay in sync with the
+ * definition in {code capi.h}.
+ */
+@CApiExternalFunctionSignatures
+public enum ExternalFunctionSignature implements NativeCExtSymbol {
+ // typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
+ PYCFUNCTION(false, PyObjectReturn, PyObject, PyObject),
+ // typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, PyObject *);
+ PYCFUNCTION_WITH_KEYWORDS(false, PyObjectReturn, PyObject, PyObject, PyObject),
+ // typedef PyObject *(*_PyCFunctionFast) (PyObject *, PyObject *const *, Py_ssize_t);
+ PYCFUNCTION_FAST(false, PyObjectReturn, PyObject, Pointer, Py_ssize_t),
+ // typedef PyObject *(*_PyCFunctionFastWithKeywords) (PyObject *, PyObject *const *, Py_ssize_t,
+ // PyObject *);
+ PYCFUNCTION_FAST_WITH_KEYWORDS(false, PyObjectReturn, PyObject, Pointer, Py_ssize_t, PyObject),
+ // typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t,
+ // PyObject *);
+ PYCMETHOD(false, PyObjectReturn, PyObject, PyTypeObject, Pointer, Py_ssize_t, PyObject),
+
+ // typedef PyObject * (*unaryfunc)(PyObject *);
+ UNARYFUNC(false, PyObjectReturn, PyObject),
+ // typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
+ BINARYFUNC(false, PyObjectReturn, PyObject, PyObject),
+ // typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
+ TERNARYFUNC(false, PyObjectReturn, PyObject, PyObject, PyObject),
+ // typedef int (*inquiry)(PyObject *);
+ INQUIRY(false, Int, PyObject),
+ // typedef Py_ssize_t (*lenfunc)(PyObject *);
+ LENFUNC(false, PrimitiveResult64, PyObject),
+ // typedef PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t);
+ SSIZEARGFUNC(false, PyObjectReturn, PyObject, Py_ssize_t),
+
+ // typedef PyObject *(*ssizessizeargfunc)(PyObject *, Py_ssize_t, Py_ssize_t);
+ SSIZESSIZEARGFUNC(false, PyObjectReturn, PyObject, Py_ssize_t, Py_ssize_t),
+ // typedef int(*ssizeobjargproc)(PyObject *, Py_ssize_t, PyObject *);
+ SSIZEOBJARGPROC(false, Int, PyObject, Py_ssize_t, PyObject),
+ // typedef int(*ssizessizeobjargproc)(PyObject *, Py_ssize_t, Py_ssize_t, PyObject *);
+ SSIZESSIZEOBJARGPROC(false, Int, PyObject, Py_ssize_t, Py_ssize_t, PyObject),
+ // typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);
+ OBJOBJARGPROC(false, Int, PyObject, PyObject, PyObject),
+
+ // typedef int (*objobjproc)(PyObject *, PyObject *);
+ OBJOBJPROC(false, Int, PyObject, PyObject),
+ // typedef int (*visitproc)(PyObject *, void *);
+ VISITPROC(false, Int, PyObject, Pointer),
+ // typedef int (*traverseproc)(PyObject *, visitproc, void *);
+ TRAVERSEPROC(false, Int, PyObject, Pointer, Pointer),
+ // PyObject *PyType_GenericAlloc(PyTypeObject *, Py_ssize_t);
+ TYPE_GENERIC_ALLOC(false, PyObjectReturn, PyTypeObject, Py_ssize_t),
+ // PyObject *PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *);
+ PY_TYPE_GENERIC_NEW(false, PyObjectReturn, PyTypeObject, PyObject, PyObject),
+ // uintptr_t PyType_GenericNew(PyTypeObject *, uintptr_t, uintptr_t);
+ PY_TYPE_GENERIC_NEW_RAW(true, UINTPTR_T, PyTypeObject, UINTPTR_T, UINTPTR_T),
+
+ // typedef void (*freefunc)(void *);
+ FREEFUNC(false, Void, Pointer),
+ // typedef void (*destructor)(PyObject *);
+ DESTRUCTOR(false, Void, PyObject),
+ // void _Py_Dealloc(PyObject *);
+ PY_DEALLOC(false, Void, PyObject),
+ // typedef PyObject *(*getattrfunc)(PyObject *, char *);
+ GETATTRFUNC(false, PyObjectReturn, PyObject, CharPtrAsTruffleString),
+ // typedef PyObject *(*getattrofunc)(PyObject *, PyObject *);
+ GETATTROFUNC(false, PyObjectReturn, PyObject, PyObject),
+ // typedef int (*setattrfunc)(PyObject *, char *, PyObject *);
+ SETATTRFUNC(false, Int, PyObject, CharPtrAsTruffleString, PyObject),
+ // typedef int (*setattrofunc)(PyObject *, PyObject *, PyObject *);
+ SETATTROFUNC(false, Int, PyObject, PyObject, PyObject),
+ // typedef PyObject *(*reprfunc)(PyObject *);
+ REPRFUNC(false, PyObjectReturn, PyObject),
+ // typedef Py_hash_t (*hashfunc)(PyObject *);
+ HASHFUNC(false, Py_ssize_t, PyObject),
+ // typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int);
+ RICHCMPFUNC(false, PyObjectReturn, PyObject, PyObject, Int),
+ // typedef PyObject *(*getiterfunc) (PyObject *);
+ GETITERFUNC(false, PyObjectReturn, PyObject),
+ // typedef PyObject *(*iternextfunc) (PyObject *);
+ ITERNEXTFUNC(false, PyObjectReturn, PyObject),
+ // typedef PyObject *(*descrgetfunc) (PyObject *, PyObject *, PyObject *);
+ DESCRGETFUNC(false, PyObjectReturn, PyObject, PyObject, PyObject),
+ // typedef int (*descrsetfunc) (PyObject *, PyObject *, PyObject *);
+ DESCRSETFUNC(false, Int, PyObject, PyObject, PyObject),
+ // typedef int (*initproc)(PyObject *, PyObject *, PyObject *);
+ INITPROC(false, Int, PyObject, PyObject, PyObject),
+ // typedef PyObject *(*newfunc)(PyTypeObject *, PyObject *, PyObject *);
+ NEWFUNC(false, PyObjectReturn, PyTypeObject, PyObject, PyObject),
+
+ // typedef PyObject *(*getter)(PyObject *, void *);
+ GETTER(false, PyObjectReturn, PyObject, Pointer),
+ // typedef int (*setter)(PyObject *, PyObject *, void *);
+ SETTER(false, Int, PyObject, PyObject, Pointer),
+ // typedef PyObject *(*Py_mod_create)(PyObject *, PyModuleDef *);
+ MODCREATE(false, PyObjectReturn, Pointer, Pointer),
+ // typedef int (*Py_mod_exec)(PyObject *);
+ MODEXEC(false, Int, Pointer),
+ // typedef PyObject *(*PyInit_mod)(void);
+ MODINIT(false, Pointer),
+ // typedef PThreadState** (*initialize_graal_capi)(void *, void *, void *);
+ CAPIINIT(false, Pointer, Pointer, Pointer, Pointer),
+ // PyThreadState **GraalPyPrivate_InitThreadStateCurrent(PyThreadState *tstate)
+ INIT_THREAD_STATE_CURRENT(true, Pointer, PyThreadState),
+ // typedef void *(*GraalPyPrivate_GetFinalizeCApiPointer)(void);
+ GETFINALIZECAPIPOINTER(false, Pointer),
+ // int PyType_Ready(PyTypeObject *);
+ PY_TYPE_READY(false, Int, PyTypeObject),
+ // void GraalPyPrivate_CheckTypeReady(PyTypeObject *);
+ TRUFFLE_CHECK_TYPE_READY(true, Void, PyTypeObject),
+ // Py_ssize_t PyUnicode_GetLength(PyObject *);
+ PY_UNICODE_GET_LENGTH(false, PrimitiveResult64, PyObject),
+ // void GraalPyPrivate_InitNativeDateTime(void);
+ INIT_NATIVE_DATETIME(true, Void),
+ // intptr_t* GraalPyPrivate_Constants(void);
+ PYTRUFFLE_CONSTANTS(true, Pointer),
+ // intptr_t* GraalPyPrivate_StructOffsets(void);
+ PYTRUFFLE_STRUCT_OFFSETS(true, Pointer),
+ // intptr_t* GraalPyPrivate_StructSizes(void);
+ PYTRUFFLE_STRUCT_SIZES(true, Pointer),
+ // void* PyMem_Calloc(size_t, size_t);
+ PYMEM_ALLOC(true, Pointer, SIZE_T, SIZE_T),
+ // int GraalPyPrivate_NoOpClear(PyObject *);
+ NO_OP_CLEAR(true, Int, PyObject),
+ // int GraalPyPrivate_NoOpTraverse(PyObject *, void *, void *);
+ NO_OP_TRAVERSE(true, Int, PyObject, Pointer, Pointer),
+ // int PyObject_GenericSetDict(PyObject *, PyObject *, void *);
+ PY_OBJECT_GENERIC_SET_DICT(false, Int, PyObject, PyObject, Pointer),
+ // PyObject *GraalPyPrivate_ObjectNew(PyTypeObject *);
+ PY_OBJECT_NEW(false, PyObjectReturn, PyTypeObject),
+ // void PyObject_Free(void *);
+ PY_OBJECT_FREE(true, Void, Pointer),
+ // void GraalPyPrivate_ObjectArrayRelease(void *, int);
+ OBJECT_ARRAY_RELEASE(true, Void, Pointer, Int),
+ // void GraalPyPrivate_Object_GC_Del(void *);
+ GRAALPY_OBJECT_GC_DEL(true, Void, Pointer),
+ // void GraalPyPrivate_Capsule_CallDestructor(PyObject *, PyCapsule_Destructor);
+ GRAALPY_CAPSULE_CALL_DESTRUCTOR(true, Void, PyObject, Pointer),
+ // Py_ssize_t GraalPyPrivate_BulkDealloc(uintptr_t, int64_t);
+ BULK_DEALLOC(true, Py_ssize_t, UINTPTR_T, INT64_T),
+ // Py_ssize_t GraalPyPrivate_BulkDeallocOnShutdown(void *, int64_t);
+ SHUTDOWN_BULK_DEALLOC(true, Py_ssize_t, Pointer, INT64_T),
+ // size_t GraalPyPrivate_GetCurrentRSS(void);
+ GET_CURRENT_RSS(true, SIZE_T),
+ // void GraalPyPrivate_ReleaseBuffer(void *);
+ GRAALPY_RELEASE_BUFFER(true, Void, Pointer),
+ // void GraalPyPrivate_MMap_InitBufferProtocol(PyTypeObject *);
+ MMAP_INIT_BUFFERPROTOCOL(true, Void, PyTypeObject),
+ // PyObject *GraalPyPrivate_Exception_SubtypeNew(PyTypeObject *, PyObject *);
+ EXCEPTION_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, PyObject),
+ // PyObject *GraalPyPrivate_Bytes_SubtypeNew(PyTypeObject *, void *, Py_ssize_t);
+ BYTES_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, Pointer, Py_ssize_t),
+ // PyObject *GraalPyPrivate_Float_SubtypeNew(PyTypeObject *, double);
+ FLOAT_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, Double),
+ // PyObject *GraalPyPrivate_Complex_SubtypeFromDoubles(PyTypeObject *, double, double);
+ COMPLEX_SUBTYPE_FROM_DOUBLES(false, PyObjectReturn, PyTypeObject, Double, Double),
+ // PyObject *GraalPyPrivate_Tuple_SubtypeNew(PyTypeObject *, PyObject *);
+ TUPLE_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, PyObject),
+ // PyObject *GraalPyPrivate_Unicode_SubtypeNew(PyTypeObject *, PyObject *);
+ UNICODE_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, PyObject),
+ // int GraalPyPrivate_CheckBasicsizeForGetstate(PyTypeObject *, int);
+ CHECK_BASICSIZE_FOR_GETSTATE(true, Int, PyTypeObject, Int),
+ // PyObject *GraalPyPrivate_Date_SubtypeNew(PyTypeObject *, int, int, int);
+ DATE_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, Int, Int, Int),
+ // PyObject *GraalPyPrivate_Time_SubtypeNew(PyTypeObject *, int, int, int, int, PyObject *,
+ // int);
+ TIME_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, Int, Int, Int, Int, PyObject, Int),
+ // PyObject *GraalPyPrivate_TimeDelta_SubtypeNew(PyTypeObject *, int, int, int);
+ TIMEDELTA_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, Int, Int, Int),
+ // PyObject *GraalPyPrivate_DateTime_SubtypeNew(PyTypeObject *, int, int, int, int, int, int,
+ // int, PyObject *, int);
+ DATETIME_SUBTYPE_NEW(false, PyObjectReturn, PyTypeObject, Int, Int, Int, Int, Int, Int, Int, PyObject, Int),
+ // PyObject *GraalPyPrivate_MemoryViewFromObject(PyObject *, int);
+ GRAALPY_MEMORYVIEW_FROM_OBJECT(false, PyObjectReturn, PyObject, Int),
+ // Py_hash_t PyObject_HashNotImplemented(PyObject *);
+ PYOBJECT_HASH_NOT_IMPLEMENTED(false, Py_ssize_t, PyObject),
+ // Py_ssize_t _PyGC_CollectNoFail(PyThreadState *);
+ PY_GC_COLLECT_NO_FAIL(true, Py_ssize_t, PyThreadState),
+ // PyObject *_PyObject_NextNotImplemented(PyObject *);
+ PY_OBJECT_NEXT_NOT_IMPLEMENTED(false, PyObjectTransfer, PyObject),
+ // int GraalPyPrivate_SubtypeTraverse(PyObject *, void *, void *);
+ SUBTYPE_TRAVERSE(true, Int, PyObject, Pointer, Pointer),
+
+ // TODO(fa): should be an implicit signature
+ GCCOLLECT(false, Py_ssize_t, Int),
+ GETDICTPTRFUN(true, Pointer, PyObject);
+
+ public final ArgDescriptor returnValue;
+ public final ArgDescriptor[] arguments;
+
+ /**
+ * If {@code true}, the function will be called without a call boundary (see
+ * {@link com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext}). Hence, the
+ * native function must not raise Python exception.
+ */
+ public final boolean cannotRaise;
+
+ ExternalFunctionSignature(boolean cannotRaise, ArgDescriptor returnValue, ArgDescriptor... arguments) {
+ this.cannotRaise = cannotRaise;
+ this.returnValue = returnValue;
+ this.arguments = arguments;
+ }
+
+ @Override
+ public String getName() {
+ return name();
+ }
+
+ @Override
+ public TruffleString getTsName() {
+ return toTruffleStringUncached(name());
+ }
+
+ @Override
+ public ArgDescriptor getReturnValue() {
+ return returnValue;
+ }
+
+ @Override
+ public ArgDescriptor[] getArguments() {
+ return arguments;
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ManagedMethodWrappers.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ManagedMethodWrappers.java
deleted file mode 100644
index 25c8271614..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ManagedMethodWrappers.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (c) 2018, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.checkThrowableBeforeNative;
-
-import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonStructNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeNode;
-import com.oracle.graal.python.builtins.objects.function.PKeyword;
-import com.oracle.graal.python.nodes.argument.keywords.ExpandKeywordStarargsNode;
-import com.oracle.graal.python.nodes.argument.positional.ExecutePositionalStarargsNode;
-import com.oracle.graal.python.nodes.call.CallNode;
-import com.oracle.graal.python.runtime.GilNode;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.graal.python.runtime.exception.PException;
-import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
-import com.oracle.truffle.api.interop.ArityException;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.library.CachedLibrary;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.nfi.api.SignatureLibrary;
-
-/**
- * Wrappers for methods used by native code.
- */
-public abstract class ManagedMethodWrappers {
-
- @ExportLibrary(InteropLibrary.class)
- public abstract static class MethodWrapper extends PythonStructNativeWrapper {
-
- public MethodWrapper(Object method) {
- super(method);
- }
-
- @ExportMessage
- public boolean isPointer() {
- return isNative();
- }
-
- @ExportMessage
- public long asPointer() {
- return getNativePointer();
- }
-
- @ExportMessage
- @TruffleBoundary
- public void toNative(
- @CachedLibrary(limit = "1") SignatureLibrary signatureLibrary) {
- if (!isPointer()) {
- CApiContext cApiContext = PythonContext.get(null).getCApiContext();
- setNativePointer(cApiContext.registerClosure(getSignature(), this, getDelegate(), signatureLibrary));
- }
- }
-
- protected abstract String getSignature();
- }
-
- @ExportLibrary(InteropLibrary.class)
- static final class MethKeywords extends MethodWrapper {
-
- public MethKeywords(Object method) {
- super(method);
- }
-
- @ExportMessage
- @SuppressWarnings("static-method")
- protected boolean isExecutable() {
- return true;
- }
-
- @ExportMessage
- public Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Exclusive @Cached NativeToPythonNode toJavaNode,
- @Exclusive @Cached PythonToNativeNewRefNode toSulongNode,
- @Exclusive @Cached CallNode callNode,
- @Exclusive @Cached ExecutePositionalStarargsNode posStarargsNode,
- @Exclusive @Cached ExpandKeywordStarargsNode expandKwargsNode,
- @Exclusive @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- try {
- if (arguments.length != 3) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, 3, arguments.length);
- }
-
- try {
- // convert args
- Object receiver = toJavaNode.execute(arguments[0]);
- Object starArgs = toJavaNode.execute(arguments[1]);
- Object kwArgs = toJavaNode.execute(arguments[2]);
-
- Object[] pArgs;
- if (starArgs != PNone.NO_VALUE) {
- Object[] starArgsArray = posStarargsNode.executeWith(null, starArgs);
- pArgs = PythonUtils.prependArgument(receiver, starArgsArray);
- } else {
- pArgs = new Object[]{receiver};
- }
- PKeyword[] kwArgsArray = expandKwargsNode.execute(inliningTarget, kwArgs);
-
- // execute
- return toSulongNode.execute(callNode.execute(null, getDelegate(), pArgs, kwArgsArray));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "MethKeywords", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(callNode).getNativeNull();
- } finally {
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):POINTER";
- }
- }
-
- /**
- * Creates a wrapper for signature {@code meth(*args, **kwargs)}.
- */
- public static MethodWrapper createKeywords(Object method) {
- return new MethKeywords(method);
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/MethodDescriptorWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/MethodDescriptorWrapper.java
new file mode 100644
index 0000000000..0e1908e3d8
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/MethodDescriptorWrapper.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2026, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.builtins.objects.cext.capi;
+
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectConstArray;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectReturn;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObject;
+import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
+
+import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.MethFastcallRoot;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.MethFastcallWithKeywordsRoot;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.MethKeywordsRoot;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.MethMethodRoot;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.MethNoargsRoot;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.MethORoot;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.MethVarargsRoot;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
+import com.oracle.graal.python.builtins.objects.cext.common.NativeCExtSymbol;
+import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
+import com.oracle.graal.python.builtins.objects.function.PKeyword;
+import com.oracle.graal.python.nodes.PRootNode;
+import com.oracle.graal.python.runtime.object.PFactory;
+import com.oracle.graal.python.util.Function;
+import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.TruffleLogger;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.strings.TruffleString;
+
+public enum MethodDescriptorWrapper implements NativeCExtSymbol {
+ // METH_FASTCALL
+ FASTCALL(PyObjectReturn, PyObject, PyObjectConstArray, Py_ssize_t),
+
+ // METH_FASTCALL | METH_KEYWORDS
+ FASTCALL_WITH_KEYWORDS(PyObjectReturn, PyObject, PyObjectConstArray, Py_ssize_t, PyObject),
+
+ // METH_VARARGS | METH_KEYWORDS
+ KEYWORDS(PyObjectReturn, PyObject, PyObject, PyObject),
+
+ // METH_VARARGS
+ VARARGS(PyObjectReturn, PyObject, PyObject),
+
+ // METH_NOARGS
+ NOARGS(PyObjectReturn, PyObject, PyObject),
+
+ // METH_O
+ O(PyObjectReturn, PyObject, PyObject),
+
+ // METH_FASTCALL | METH_KEYWORDS | METH_METHOD:
+ METHOD(PyObjectReturn, PyObject, PyTypeObject, PyObjectConstArray, Py_ssize_t, PyObject);
+
+ public final ArgDescriptor returnValue;
+ public final ArgDescriptor[] arguments;
+
+ MethodDescriptorWrapper(ArgDescriptor returnValue, ArgDescriptor... arguments) {
+ this.returnValue = returnValue;
+ this.arguments = arguments;
+ }
+
+ @Override
+ public String getName() {
+ return name();
+ }
+
+ @Override
+ public TruffleString getTsName() {
+ throw CompilerDirectives.shouldNotReachHere();
+ }
+
+ @Override
+ public ArgDescriptor getReturnValue() {
+ return returnValue;
+ }
+
+ @Override
+ public ArgDescriptor[] getArguments() {
+ return arguments;
+ }
+
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(MethodDescriptorWrapper.class);
+
+ @TruffleBoundary
+ static RootCallTarget getOrCreateCallTarget(PythonLanguage language, MethodDescriptorWrapper sig, TruffleString name, boolean isStatic) {
+ Class extends PRootNode> nodeKlass;
+ Function rootNodeFunction = switch (sig) {
+ case KEYWORDS -> {
+ nodeKlass = MethKeywordsRoot.class;
+ yield l -> new MethKeywordsRoot(l, name, isStatic, sig);
+ }
+ case VARARGS -> {
+ nodeKlass = MethVarargsRoot.class;
+ yield (l -> new MethVarargsRoot(l, name, isStatic, sig));
+ }
+ case NOARGS -> {
+ nodeKlass = MethNoargsRoot.class;
+ yield (l -> new MethNoargsRoot(l, name, isStatic, sig));
+ }
+ case O -> {
+ nodeKlass = MethORoot.class;
+ yield (l -> new MethORoot(l, name, isStatic, sig));
+ }
+ case FASTCALL -> {
+ nodeKlass = MethFastcallRoot.class;
+ yield (l -> new MethFastcallRoot(l, name, isStatic, sig));
+ }
+ case FASTCALL_WITH_KEYWORDS -> {
+ nodeKlass = MethFastcallWithKeywordsRoot.class;
+ yield (l -> new MethFastcallWithKeywordsRoot(l, name, isStatic, sig));
+ }
+ case METHOD -> {
+ nodeKlass = MethMethodRoot.class;
+ yield (l -> new MethMethodRoot(l, name, isStatic, sig));
+ }
+ };
+ return language.createCachedExternalFunWrapperCallTarget(rootNodeFunction, nodeKlass, sig, name, true, isStatic);
+ }
+
+ /**
+ * Similar to {@code PyDescr_NewMethod}, creates a built-in function for a specific signature.
+ * This built-in function also does appropriate argument and result conversion and calls the
+ * provided callable.
+ *
+ * @param language The Python language object.
+ * @param name The name of the method.
+ * @param callable A reference denoting executable code. Currently, there are two
+ * representations for that: a native function pointer or a {@link RootCallTarget}
+ * @param enclosingType The type the function belongs to (needed for checking of {@code self}).
+ * @return A {@link PBuiltinFunction} implementing the semantics of the specified slot wrapper.
+ */
+ @TruffleBoundary
+ public static PBuiltinFunction createWrapperFunction(PythonLanguage language, TruffleString name, long callable, Object enclosingType, int flags) {
+ LOGGER.finer(() -> PythonUtils.formatJString("MethodDescriptorWrapper.createWrapperFunction(%s, %s)", name, callable));
+ MethodDescriptorWrapper methodDescriptorWrapper = fromMethodFlags(flags);
+ if (methodDescriptorWrapper == null) {
+ return null;
+ }
+
+ RootCallTarget callTarget = getOrCreateCallTarget(language, methodDescriptorWrapper, name, CExtContext.isMethStatic(flags));
+
+ PKeyword[] kwDefaults = ExternalFunctionNodes.createKwDefaults(CExtCommonNodes.bindFunctionPointer(callable, methodDescriptorWrapper));
+
+ // generate default values for positional args (if necessary)
+ Object[] defaults = PBuiltinFunction.generateDefaults(0);
+
+ Object type = enclosingType == PNone.NO_VALUE ? null : enclosingType;
+ return PFactory.createBuiltinFunction(language, name, type, defaults, kwDefaults, flags, callTarget);
+ }
+
+ /**
+ * See {@code PyDescr_NewMethod}
+ *
+ * @param flags The method flags {@link CExtContext#METH_VARARGS} and others.
+ * @return
+ */
+ static MethodDescriptorWrapper fromMethodFlags(int flags) {
+ if (CExtContext.isMethVarargs(flags)) {
+ return VARARGS;
+ }
+ if (CExtContext.isMethVarargsWithKeywords(flags)) {
+ return KEYWORDS;
+ }
+ if (CExtContext.isMethFastcall(flags)) {
+ return FASTCALL;
+ }
+ if (CExtContext.isMethFastcallWithKeywords(flags)) {
+ return FASTCALL_WITH_KEYWORDS;
+ }
+ if (CExtContext.isMethNoArgs(flags)) {
+ return NOARGS;
+ }
+ if (CExtContext.isMethO(flags)) {
+ return O;
+ }
+ if (CExtContext.isMethMethod(flags)) {
+ return METHOD;
+ }
+ throw CompilerDirectives.shouldNotReachHere("illegal method flags");
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java
index a45b84c36b..5684c8bc4c 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java
@@ -40,18 +40,6 @@
*/
package com.oracle.graal.python.builtins.objects.cext.capi;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.INT64_T;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Int;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.IterResult;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_SSIZE_T_PTR;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Pointer;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObject;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyThreadState;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObject;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.SIZE_T;
-import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void;
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
@@ -61,109 +49,74 @@
public enum NativeCAPISymbol implements NativeCExtSymbol {
- FUN_VA_ARG_POINTER("GraalPyPrivate_VaArgPointer", Pointer, Pointer),
- FUN_NO_OP_CLEAR("GraalPyPrivate_NoOpClear", Int, PyObject),
- FUN_NO_OP_TRAVERSE("GraalPyPrivate_NoOpTraverse", Int, PyObject, Pointer, Pointer),
+ FUN_NO_OP_CLEAR("GraalPyPrivate_NoOpClear", ExternalFunctionSignature.NO_OP_CLEAR),
+ FUN_NO_OP_TRAVERSE("GraalPyPrivate_NoOpTraverse", ExternalFunctionSignature.NO_OP_TRAVERSE),
- FUN_PYTRUFFLE_CONSTANTS("GraalPyPrivate_Constants", PY_SSIZE_T_PTR),
- FUN_PYTRUFFLE_STRUCT_OFFSETS("GraalPyPrivate_StructOffsets", PY_SSIZE_T_PTR),
- FUN_PYTRUFFLE_STRUCT_SIZES("GraalPyPrivate_StructSizes", PY_SSIZE_T_PTR),
-
- /* C functions for reading native members by offset */
-
- FUN_READ_SHORT_MEMBER("GraalPyPrivate_ReadShortMember", Int, Pointer, Py_ssize_t),
- FUN_READ_INT_MEMBER("GraalPyPrivate_ReadIntMember", Int, Pointer, Py_ssize_t),
- FUN_READ_LONG_MEMBER("GraalPyPrivate_ReadLongMember", ArgDescriptor.Long, Pointer, Py_ssize_t),
- FUN_READ_FLOAT_MEMBER("GraalPyPrivate_ReadFloatMember", ArgDescriptor.Double, Pointer, Py_ssize_t),
- FUN_READ_DOUBLE_MEMBER("GraalPyPrivate_ReadDoubleMember", ArgDescriptor.Double, Pointer, Py_ssize_t),
- FUN_READ_POINTER_MEMBER("GraalPyPrivate_ReadPointerMember", Pointer, Pointer, Py_ssize_t),
- FUN_READ_CHAR_MEMBER("GraalPyPrivate_ReadCharMember", Int, Pointer, Py_ssize_t),
-
- /* C functions for writing native members by offset */
-
- FUN_WRITE_SHORT_MEMBER("GraalPyPrivate_WriteShortMember", Int, Pointer, Py_ssize_t, Int),
- FUN_WRITE_INT_MEMBER("GraalPyPrivate_WriteIntMember", Int, Pointer, Py_ssize_t, Int),
- FUN_WRITE_LONG_MEMBER("GraalPyPrivate_WriteLongMember", Int, Pointer, Py_ssize_t, ArgDescriptor.Long),
- FUN_WRITE_FLOAT_MEMBER("GraalPyPrivate_WriteFloatMember", Int, Pointer, Py_ssize_t, ArgDescriptor.Double),
- FUN_WRITE_DOUBLE_MEMBER("GraalPyPrivate_WriteDoubleMember", Int, Pointer, Py_ssize_t, ArgDescriptor.Double),
- FUN_WRITE_OBJECT_MEMBER("GraalPyPrivate_WriteObjectMember", Int, Pointer, Py_ssize_t, Pointer),
- FUN_WRITE_POINTER_MEMBER("GraalPyPrivate_WritePointerMember", Int, Pointer, Py_ssize_t, Pointer),
- FUN_WRITE_CHAR_MEMBER("GraalPyPrivate_WriteByteMember", Int, Pointer, Py_ssize_t, Int),
+ FUN_PYTRUFFLE_CONSTANTS("GraalPyPrivate_Constants", ExternalFunctionSignature.PYTRUFFLE_CONSTANTS),
+ FUN_PYTRUFFLE_STRUCT_OFFSETS("GraalPyPrivate_StructOffsets", ExternalFunctionSignature.PYTRUFFLE_STRUCT_OFFSETS),
+ FUN_PYTRUFFLE_STRUCT_SIZES("GraalPyPrivate_StructSizes", ExternalFunctionSignature.PYTRUFFLE_STRUCT_SIZES),
/* Python C API functions */
- FUN_PY_TYPE_READY("PyType_Ready", Int, PyTypeObject),
- FUN_PY_OBJECT_FREE("PyObject_Free", Void, Pointer),
- FUN_PY_OBJECT_GENERIC_SET_DICT("PyObject_GenericSetDict", Int, PyObject, PyObject, Pointer),
- FUN_PY_TYPE_GENERIC_NEW("PyType_GenericNew", PyObjectTransfer, PyTypeObject, PyObject, PyObject),
- FUN_PY_TYPE_GENERIC_NEW_RAW("PyType_GenericNew", ArgDescriptor.UINTPTR_T, PyTypeObject, ArgDescriptor.UINTPTR_T, ArgDescriptor.UINTPTR_T),
- FUN_PY_TYPE_GENERIC_ALLOC("PyType_GenericAlloc", PyObjectTransfer, PyTypeObject, Py_ssize_t),
- FUN_PY_OBJECT_GET_DICT_PTR("_PyObject_GetDictPtr", Pointer, PyObject),
- FUN_PY_UNICODE_GET_LENGTH("PyUnicode_GetLength", Py_ssize_t, PyObject),
- FUN_PYMEM_ALLOC("PyMem_Calloc", Pointer, SIZE_T, SIZE_T),
- FUN_PY_DEALLOC("_Py_Dealloc", Void, Pointer),
- FUN_PYOBJECT_HASH_NOT_IMPLEMENTED("PyObject_HashNotImplemented", ArgDescriptor.Py_hash_t, PyObject),
- FUN_PY_GC_COLLECT_NO_FAIL("_PyGC_CollectNoFail", Py_ssize_t, PyThreadState),
- FUN_PY_OBJECT_NEXT_NOT_IMPLEMENTED("_PyObject_NextNotImplemented", IterResult, PyObject),
+ FUN_PY_TYPE_READY("PyType_Ready", ExternalFunctionSignature.PY_TYPE_READY),
+ FUN_PY_OBJECT_FREE("PyObject_Free", ExternalFunctionSignature.PY_OBJECT_FREE),
+ FUN_PY_OBJECT_GENERIC_SET_DICT("PyObject_GenericSetDict", ExternalFunctionSignature.PY_OBJECT_GENERIC_SET_DICT),
+ FUN_PY_TYPE_GENERIC_NEW("PyType_GenericNew", ExternalFunctionSignature.PY_TYPE_GENERIC_NEW),
+ FUN_PY_TYPE_GENERIC_NEW_RAW("PyType_GenericNew", ExternalFunctionSignature.PY_TYPE_GENERIC_NEW_RAW),
+ FUN_PY_TYPE_GENERIC_ALLOC("PyType_GenericAlloc", ExternalFunctionSignature.TYPE_GENERIC_ALLOC),
+ FUN_PY_OBJECT_GET_DICT_PTR("_PyObject_GetDictPtr", ExternalFunctionSignature.GETDICTPTRFUN),
+ FUN_PY_UNICODE_GET_LENGTH("PyUnicode_GetLength", ExternalFunctionSignature.PY_UNICODE_GET_LENGTH),
+ FUN_PYMEM_ALLOC("PyMem_Calloc", ExternalFunctionSignature.PYMEM_ALLOC),
+ FUN_PY_DEALLOC("_Py_Dealloc", ExternalFunctionSignature.PY_DEALLOC),
+ FUN_PYOBJECT_HASH_NOT_IMPLEMENTED("PyObject_HashNotImplemented", ExternalFunctionSignature.PYOBJECT_HASH_NOT_IMPLEMENTED),
+ FUN_PY_GC_COLLECT_NO_FAIL("_PyGC_CollectNoFail", ExternalFunctionSignature.PY_GC_COLLECT_NO_FAIL),
+ FUN_PY_OBJECT_NEXT_NOT_IMPLEMENTED("_PyObject_NextNotImplemented", ExternalFunctionSignature.PY_OBJECT_NEXT_NOT_IMPLEMENTED),
/* GraalPy-specific helper functions */
- FUN_PTR_COMPARE("GraalPyPrivate_PointerCompare", Int, Pointer, Pointer, Int),
- FUN_PTR_ADD("GraalPyPrivate_PointerAddOffset", Pointer, Pointer, Py_ssize_t),
- FUN_OBJECT_ARRAY_RELEASE("GraalPyPrivate_ObjectArrayRelease", ArgDescriptor.Void, Pointer, Int),
- FUN_PY_OBJECT_NEW("GraalPyPrivate_ObjectNew", PyObjectTransfer, PyTypeObject),
- FUN_GRAALPY_OBJECT_GC_DEL("GraalPyPrivate_Object_GC_Del", Void, Pointer),
- FUN_BULK_DEALLOC("GraalPyPrivate_BulkDealloc", Py_ssize_t, ArgDescriptor.UINTPTR_T, INT64_T),
- FUN_SHUTDOWN_BULK_DEALLOC("GraalPyPrivate_BulkDeallocOnShutdown", Py_ssize_t, Pointer, INT64_T),
- FUN_GET_CURRENT_RSS("GraalPyPrivate_GetCurrentRSS", SIZE_T),
- FUN_ADD_SUBOFFSET("GraalPyPrivate_AddSuboffset", Pointer, Pointer, Py_ssize_t, Py_ssize_t),
- FUN_GRAALPY_MEMORYVIEW_FROM_OBJECT("GraalPyPrivate_MemoryViewFromObject", PyObjectTransfer, PyObject, Int),
- FUN_GRAALPY_RELEASE_BUFFER("GraalPyPrivate_ReleaseBuffer", ArgDescriptor.Void, Pointer),
- FUN_GRAALPY_CAPSULE_CALL_DESTRUCTOR("GraalPyPrivate_Capsule_CallDestructor", ArgDescriptor.Void, PyObject, ArgDescriptor.PY_CAPSULE_DESTRUCTOR),
- FUN_TUPLE_SUBTYPE_NEW("GraalPyPrivate_Tuple_SubtypeNew", PyObjectTransfer, PyTypeObject, PyObject),
- FUN_BYTES_SUBTYPE_NEW("GraalPyPrivate_Bytes_SubtypeNew", PyObjectTransfer, PyTypeObject, Pointer, Py_ssize_t),
- FUN_FLOAT_SUBTYPE_NEW("GraalPyPrivate_Float_SubtypeNew", PyObjectTransfer, PyTypeObject, ArgDescriptor.Double),
- FUN_COMPLEX_SUBTYPE_FROM_DOUBLES("GraalPyPrivate_Complex_SubtypeFromDoubles", PyObjectTransfer, PyTypeObject, ArgDescriptor.Double, ArgDescriptor.Double),
- FUN_TIME_SUBTYPE_NEW("GraalPyPrivate_Time_SubtypeNew", PyObjectTransfer, PyTypeObject, Int, Int, Int, Int, PyObject, Int),
- FUN_DATE_SUBTYPE_NEW("GraalPyPrivate_Date_SubtypeNew", PyObjectTransfer, PyTypeObject, Int, Int, Int),
- FUN_TIMEDELTA_SUBTYPE_NEW("GraalPyPrivate_TimeDelta_SubtypeNew", PyObjectTransfer, PyTypeObject, Int, Int, Int),
- FUN_DATETIME_SUBTYPE_NEW("GraalPyPrivate_DateTime_SubtypeNew", PyObjectTransfer, PyTypeObject, Int, Int, Int, Int, Int, Int, Int, PyObject, Int),
- FUN_EXCEPTION_SUBTYPE_NEW("GraalPyPrivate_Exception_SubtypeNew", PyObjectTransfer, PyTypeObject, PyObject),
- FUN_UNICODE_SUBTYPE_NEW("GraalPyPrivate_Unicode_SubtypeNew", PyObjectTransfer, PyTypeObject, PyObject),
- FUN_CHECK_BASICSIZE_FOR_GETSTATE("GraalPyPrivate_CheckBasicsizeForGetstate", Int, PyTypeObject, Int),
- FUN_MMAP_INIT_BUFFERPROTOCOL("GraalPyPrivate_MMap_InitBufferProtocol", ArgDescriptor.Void, PyTypeObject),
- FUN_TRUFFLE_CHECK_TYPE_READY("GraalPyPrivate_CheckTypeReady", ArgDescriptor.Void, PyTypeObject),
- FUN_GRAALPY_GC_COLLECT("GraalPyPrivate_GC_Collect", Py_ssize_t, Int),
- FUN_SUBTYPE_TRAVERSE("GraalPyPrivate_SubtypeTraverse", Int, PyObject, Pointer, Pointer),
- FUN_INIT_THREAD_STATE_CURRENT("GraalPyPrivate_InitThreadStateCurrent", Pointer, PyThreadState),
+ FUN_OBJECT_ARRAY_RELEASE("GraalPyPrivate_ObjectArrayRelease", ExternalFunctionSignature.OBJECT_ARRAY_RELEASE),
+ FUN_PY_OBJECT_NEW("GraalPyPrivate_ObjectNew", ExternalFunctionSignature.PY_OBJECT_NEW),
+ FUN_GRAALPY_OBJECT_GC_DEL("GraalPyPrivate_Object_GC_Del", ExternalFunctionSignature.GRAALPY_OBJECT_GC_DEL),
+ FUN_BULK_DEALLOC("GraalPyPrivate_BulkDealloc", ExternalFunctionSignature.BULK_DEALLOC),
+ FUN_SHUTDOWN_BULK_DEALLOC("GraalPyPrivate_BulkDeallocOnShutdown", ExternalFunctionSignature.SHUTDOWN_BULK_DEALLOC),
+ FUN_GET_CURRENT_RSS("GraalPyPrivate_GetCurrentRSS", ExternalFunctionSignature.GET_CURRENT_RSS),
+ FUN_GRAALPY_MEMORYVIEW_FROM_OBJECT("GraalPyPrivate_MemoryViewFromObject", ExternalFunctionSignature.GRAALPY_MEMORYVIEW_FROM_OBJECT),
+ FUN_GRAALPY_RELEASE_BUFFER("GraalPyPrivate_ReleaseBuffer", ExternalFunctionSignature.GRAALPY_RELEASE_BUFFER),
+ FUN_GRAALPY_CAPSULE_CALL_DESTRUCTOR("GraalPyPrivate_Capsule_CallDestructor", ExternalFunctionSignature.GRAALPY_CAPSULE_CALL_DESTRUCTOR),
+ FUN_TUPLE_SUBTYPE_NEW("GraalPyPrivate_Tuple_SubtypeNew", ExternalFunctionSignature.TUPLE_SUBTYPE_NEW),
+ FUN_BYTES_SUBTYPE_NEW("GraalPyPrivate_Bytes_SubtypeNew", ExternalFunctionSignature.BYTES_SUBTYPE_NEW),
+ FUN_FLOAT_SUBTYPE_NEW("GraalPyPrivate_Float_SubtypeNew", ExternalFunctionSignature.FLOAT_SUBTYPE_NEW),
+ FUN_COMPLEX_SUBTYPE_FROM_DOUBLES("GraalPyPrivate_Complex_SubtypeFromDoubles", ExternalFunctionSignature.COMPLEX_SUBTYPE_FROM_DOUBLES),
+ FUN_TIME_SUBTYPE_NEW("GraalPyPrivate_Time_SubtypeNew", ExternalFunctionSignature.TIME_SUBTYPE_NEW),
+ FUN_DATE_SUBTYPE_NEW("GraalPyPrivate_Date_SubtypeNew", ExternalFunctionSignature.DATE_SUBTYPE_NEW),
+ FUN_TIMEDELTA_SUBTYPE_NEW("GraalPyPrivate_TimeDelta_SubtypeNew", ExternalFunctionSignature.TIMEDELTA_SUBTYPE_NEW),
+ FUN_DATETIME_SUBTYPE_NEW("GraalPyPrivate_DateTime_SubtypeNew", ExternalFunctionSignature.DATETIME_SUBTYPE_NEW),
+ FUN_EXCEPTION_SUBTYPE_NEW("GraalPyPrivate_Exception_SubtypeNew", ExternalFunctionSignature.EXCEPTION_SUBTYPE_NEW),
+ FUN_UNICODE_SUBTYPE_NEW("GraalPyPrivate_Unicode_SubtypeNew", ExternalFunctionSignature.UNICODE_SUBTYPE_NEW),
+ FUN_CHECK_BASICSIZE_FOR_GETSTATE("GraalPyPrivate_CheckBasicsizeForGetstate", ExternalFunctionSignature.CHECK_BASICSIZE_FOR_GETSTATE),
+ FUN_MMAP_INIT_BUFFERPROTOCOL("GraalPyPrivate_MMap_InitBufferProtocol", ExternalFunctionSignature.MMAP_INIT_BUFFERPROTOCOL),
+ FUN_TRUFFLE_CHECK_TYPE_READY("GraalPyPrivate_CheckTypeReady", ExternalFunctionSignature.TRUFFLE_CHECK_TYPE_READY),
+ FUN_GRAALPY_GC_COLLECT("GraalPyPrivate_GC_Collect", ExternalFunctionSignature.GCCOLLECT),
+ FUN_SUBTYPE_TRAVERSE("GraalPyPrivate_SubtypeTraverse", ExternalFunctionSignature.SUBTYPE_TRAVERSE),
+ FUN_INIT_THREAD_STATE_CURRENT("GraalPyPrivate_InitThreadStateCurrent", ExternalFunctionSignature.INIT_THREAD_STATE_CURRENT),
/* PyDateTime_CAPI */
- FUN_INIT_NATIVE_DATETIME("GraalPyPrivate_InitNativeDateTime", ArgDescriptor.Void);
+ FUN_INIT_NATIVE_DATETIME("GraalPyPrivate_InitNativeDateTime", ExternalFunctionSignature.INIT_NATIVE_DATETIME);
private final String name;
private final TruffleString tsName;
-
- private final String signature;
+ private final ExternalFunctionSignature signature;
@CompilationFinal(dimensions = 1) private static final NativeCAPISymbol[] VALUES = values();
- NativeCAPISymbol(String name, ArgDescriptor returnValue, ArgDescriptor... arguments) {
+ NativeCAPISymbol(String name, ExternalFunctionSignature signature) {
this.name = name;
this.tsName = toTruffleStringUncached(name);
-
- StringBuilder s = new StringBuilder("(");
- for (int i = 0; i < arguments.length; i++) {
- s.append(i == 0 ? "" : ",");
- s.append(arguments[i].getNFISignature());
- }
- s.append("):").append(returnValue.getNFISignature());
- this.signature = s.toString();
+ this.signature = signature;
}
NativeCAPISymbol(String name) {
- this.name = name;
- this.tsName = toTruffleStringUncached(name);
- this.signature = null;
+ this(name, null);
}
@Override
@@ -180,8 +133,17 @@ public static NativeCAPISymbol[] getValues() {
return VALUES;
}
- public String getSignature() {
- assert signature != null : "no signature for " + this;
+ public ExternalFunctionSignature getSignature() {
return signature;
}
+
+ public ArgDescriptor getReturnValue() {
+ return signature != null ? signature.getReturnValue() : null;
+ }
+
+ @Override
+ public ArgDescriptor[] getArguments() {
+ assert signature != null : "no signature for " + this;
+ return signature.getArguments();
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java
index a3a0cb8613..227400150f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PThreadState.java
@@ -40,23 +40,30 @@
*/
package com.oracle.graal.python.builtins.objects.cext.capi;
+import static com.oracle.graal.python.builtins.objects.PythonAbstractObject.NATIVE_POINTER_FREED;
+import static com.oracle.graal.python.builtins.objects.PythonAbstractObject.UNINITIALIZED;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.callocPtrArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.mallocPtrArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writePtrArrayElement;
+
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonStructNativeWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.ReadObjectNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.dict.PDict;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.CApiState;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.object.PFactory;
+import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.interop.InteropLibrary;
/**
@@ -68,8 +75,9 @@
* return the appropriate pointer object that implements that.
*
*/
-public final class PThreadState extends PythonStructNativeWrapper {
- private final Object replacement;
+public abstract class PThreadState {
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(PThreadState.class);
+ private static final int GRAALPY_DEALLOC_STACK_INITIAL_CAPACITY = 3;
/** Same as _PY_NSMALLNEGINTS */
public static final int PY_NSMALLNEGINTS = 5;
@@ -77,38 +85,20 @@ public final class PThreadState extends PythonStructNativeWrapper {
/** Same as _PY_NSMALLPOSINTS */
public static final int PY_NSMALLPOSINTS = 257;
- @TruffleBoundary
- private PThreadState(PythonThreadState threadState) {
- super(threadState);
- long ptr = allocateCLayout();
- CApiTransitions.createReference(this, ptr, true);
- // TODO: wrap in NativePointer for NFI
- replacement = new NativePointer(ptr);
+ private PThreadState() {
}
- public static Object getOrCreateNativeThreadState(PythonLanguage language, PythonContext context) {
+ public static long getOrCreateNativeThreadState(PythonLanguage language, PythonContext context) {
return getOrCreateNativeThreadState(context.getThreadState(language));
}
- public static Object getOrCreateNativeThreadState(PythonThreadState threadState) {
- PThreadState nativeWrapper = threadState.getNativeWrapper();
- if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, nativeWrapper == null)) {
- nativeWrapper = new PThreadState(threadState);
- threadState.setNativeWrapper(nativeWrapper);
- }
- return nativeWrapper.replacement;
- }
-
- public static Object getNativeThreadState(PythonThreadState threadState) {
- PThreadState nativeWrapper = threadState.getNativeWrapper();
- if (nativeWrapper != null) {
- return nativeWrapper.replacement;
+ public static long getOrCreateNativeThreadState(PythonThreadState threadState) {
+ long pointer = threadState.getNativePointer();
+ if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, pointer == UNINITIALIZED)) {
+ pointer = PThreadState.allocateCLayout();
+ threadState.setNativePointer(pointer);
}
- return null;
- }
-
- public PythonThreadState getThreadState() {
- return (PythonThreadState) getDelegate();
+ return pointer;
}
@TruffleBoundary
@@ -119,19 +109,19 @@ public static PDict getOrCreateThreadStateDict(PythonContext context, PythonThre
*/
assert context.getCApiState() == CApiState.INITIALIZED;
- Object nativeThreadState = PThreadState.getNativeThreadState(threadState);
- assert nativeThreadState != null;
+ long nativeThreadState = threadState.getNativePointer();
+ assert nativeThreadState != NULLPTR;
PDict threadStateDict = threadState.getDict();
if (threadStateDict != null) {
- assert threadStateDict == ReadObjectNode.getUncached().read(nativeThreadState, CFields.PyThreadState__dict);
+ assert PythonToNativeNode.executeLongUncached(threadStateDict) == CStructAccess.readPtrField(nativeThreadState, CFields.PyThreadState__dict);
return threadStateDict;
}
threadStateDict = PFactory.createDict(context.getLanguage());
threadState.setDict(threadStateDict);
- assert ReadObjectNode.getUncached().read(nativeThreadState, CFields.PyThreadState__dict) == PNone.NO_VALUE;
- CStructAccess.WritePointerNode.writeUncached(nativeThreadState, CFields.PyThreadState__dict, PythonToNativeNode.executeUncached(threadStateDict));
+ assert CStructAccess.readPtrField(nativeThreadState, CFields.PyThreadState__dict) == NULLPTR;
+ CStructAccess.writePtrField(nativeThreadState, CFields.PyThreadState__dict, PythonToNativeNode.executeLongUncached(threadStateDict));
return threadStateDict;
}
@@ -148,30 +138,80 @@ public static PDict getOrCreateThreadStateDict(PythonContext context, PythonThre
*/
@TruffleBoundary
private static long allocateCLayout() {
- long ptr = CStructAccess.AllocateNode.allocUncachedPointer(CStructs.PyThreadState.size());
- CStructAccess.WritePointerNode writePtrNode = CStructAccess.WritePointerNode.getUncached();
+ long ptr = CStructAccess.allocate(CStructs.PyThreadState);
PythonContext pythonContext = PythonContext.get(null);
/*
* As in CPython, the thread state dict is initialized lazily. This is necessary to avoid
* cycles in the bootstrapping process because creating the dict will need the GC state
* which needs the thread state.
*/
- writePtrNode.write(ptr, CFields.PyThreadState__dict, pythonContext.getNativeNull());
+ CStructAccess.writePtrField(ptr, CFields.PyThreadState__dict, NULLPTR);
CApiContext cApiContext = pythonContext.getCApiContext();
- Object smallInts = CStructAccess.AllocateNode.allocUncached((PY_NSMALLNEGINTS + PY_NSMALLPOSINTS) * CStructAccess.POINTER_SIZE);
- writePtrNode.write(ptr, CFields.PyThreadState__small_ints, smallInts);
+ long smallInts = mallocPtrArray(PY_NSMALLNEGINTS + PY_NSMALLPOSINTS);
+ long deallocatingState = CStructAccess.getFieldPtr(ptr, CFields.PyThreadState__graalpy_deallocating);
+ long deallocating = mallocPtrArray(GRAALPY_DEALLOC_STACK_INITIAL_CAPACITY);
+ CStructAccess.writePtrField(ptr, CFields.PyThreadState__small_ints, smallInts);
+ CStructAccess.writePtrField(deallocatingState, CFields.GraalPyDeallocState__items, deallocating);
for (int i = -PY_NSMALLNEGINTS; i < PY_NSMALLPOSINTS; i++) {
- writePtrNode.writeArrayElement(smallInts, i + PY_NSMALLNEGINTS, CApiTransitions.HandlePointerConverter.intToPointer(i));
+ writePtrArrayElement(smallInts, i + PY_NSMALLNEGINTS, CApiTransitions.HandlePointerConverter.intToPointer(i));
}
- writePtrNode.write(ptr, CFields.PyThreadState__gc, cApiContext.getGCState());
- CStructAccess.WriteIntNode writeIntNode = CStructAccess.WriteIntNode.getUncached();
+ CStructAccess.writePtrField(ptr, CFields.PyThreadState__gc, cApiContext.getGCState());
+ CStructAccess.writeIntField(deallocatingState, CFields.GraalPyDeallocState__len, 0);
+ CStructAccess.writeIntField(deallocatingState, CFields.GraalPyDeallocState__capacity, GRAALPY_DEALLOC_STACK_INITIAL_CAPACITY);
// py_recursion_limit = Py_DEFAULT_RECURSION_LIMIT (1000)
// (cpython/Include/internal/pycore_runtime_init.h)
int recLimit = pythonContext.getSysModuleState().getRecursionLimit();
- writeIntNode.write(ptr, CFields.PyThreadState__py_recursion_limit, recLimit);
- writeIntNode.write(ptr, CFields.PyThreadState__py_recursion_remaining, recLimit);
+ CStructAccess.writeIntField(ptr, CFields.PyThreadState__py_recursion_limit, recLimit);
+ CStructAccess.writeIntField(ptr, CFields.PyThreadState__py_recursion_remaining, recLimit);
// c_recursion_remaining = Py_C_RECURSION_LIMIT (1000) (cpython/Include/cpython/pystate.h)
- writeIntNode.write(ptr, CFields.PyThreadState__c_recursion_remaining, recLimit);
+ CStructAccess.writeIntField(ptr, CFields.PyThreadState__c_recursion_remaining, recLimit);
+ LOGGER.fine(String.format("Allocated (PyThreadState *)0x%x", ptr));
return ptr;
}
+
+ public static int growDeallocatingStack(long nativeThreadState, long newCapacity) {
+ CompilerAsserts.neverPartOfCompilation();
+ assert nativeThreadState != NULLPTR;
+ long deallocatingState = CStructAccess.getFieldPtr(nativeThreadState, CFields.PyThreadState__graalpy_deallocating);
+ long oldItems = CStructAccess.readPtrField(deallocatingState, CFields.GraalPyDeallocState__items);
+ int oldCapacity = CStructAccess.readIntField(deallocatingState, CFields.GraalPyDeallocState__capacity);
+ assert newCapacity > oldCapacity;
+ assert newCapacity <= Integer.MAX_VALUE;
+
+ long newItems;
+ try {
+ newItems = callocPtrArray(newCapacity);
+ } catch (OutOfMemoryError e) {
+ return -1;
+ }
+
+ if (oldItems != NULLPTR) {
+ NativeMemory.memcpy(newItems, oldItems, oldCapacity * NativeMemory.POINTER_SIZE);
+ NativeMemory.free(oldItems);
+ }
+ CStructAccess.writePtrField(deallocatingState, CFields.GraalPyDeallocState__items, newItems);
+ CStructAccess.writeIntField(deallocatingState, CFields.GraalPyDeallocState__capacity, (int) newCapacity);
+ return 0;
+ }
+
+ @TruffleBoundary
+ public static void dispose(PythonThreadState threadState) {
+ long nativeCompanion = threadState.getNativePointer();
+ if (nativeCompanion == UNINITIALIZED || nativeCompanion == NATIVE_POINTER_FREED) {
+ return;
+ }
+
+ assert !HandlePointerConverter.pointsToPyHandleSpace(nativeCompanion);
+ threadState.clearNativePointer();
+
+ long deallocatingState = CStructAccess.getFieldPtr(nativeCompanion, CFields.PyThreadState__graalpy_deallocating);
+ long deallocatingItems = CStructAccess.readPtrField(deallocatingState, CFields.GraalPyDeallocState__items);
+ if (deallocatingItems != NULLPTR) {
+ NativeMemory.free(deallocatingItems);
+ }
+
+ // TODO(fa): decref PyThreadState__dict
+ LOGGER.fine(String.format("Freeing (PyThreadState *)0x%x", nativeCompanion));
+ NativeMemory.free(nativeCompanion);
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PrimitiveNativeWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PrimitiveNativeWrapper.java
deleted file mode 100644
index 55144c78ed..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PrimitiveNativeWrapper.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (c) 2018, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-// skip GIL
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.MaterializeDelegateNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
-import com.oracle.graal.python.builtins.objects.ints.PInt;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.utilities.TriState;
-
-@ExportLibrary(InteropLibrary.class)
-public final class PrimitiveNativeWrapper extends PythonAbstractObjectNativeWrapper {
-
- public static final byte PRIMITIVE_STATE_BOOL = 1;
- public static final byte PRIMITIVE_STATE_INT = 1 << 2;
- public static final byte PRIMITIVE_STATE_LONG = 1 << 3;
- public static final byte PRIMITIVE_STATE_DOUBLE = 1 << 4;
-
- private final byte state;
- private final long value;
- private final double dvalue;
-
- private PrimitiveNativeWrapper(byte state, long value) {
- assert state != PRIMITIVE_STATE_DOUBLE;
- this.state = state;
- this.value = value;
- this.dvalue = 0.0;
- }
-
- private PrimitiveNativeWrapper(double dvalue) {
- this.state = PRIMITIVE_STATE_DOUBLE;
- this.value = 0;
- this.dvalue = dvalue;
- }
-
- public byte getState() {
- return state;
- }
-
- public boolean getBool() {
- return value != 0;
- }
-
- public int getInt() {
- return (int) value;
- }
-
- public long getLong() {
- return value;
- }
-
- public double getDouble() {
- return dvalue;
- }
-
- public boolean isBool() {
- return state == PRIMITIVE_STATE_BOOL;
- }
-
- public boolean isInt() {
- return state == PRIMITIVE_STATE_INT;
- }
-
- public boolean isLong() {
- return state == PRIMITIVE_STATE_LONG;
- }
-
- public boolean isDouble() {
- return state == PRIMITIVE_STATE_DOUBLE;
- }
-
- public boolean isIntLike() {
- return (state & (PRIMITIVE_STATE_INT | PRIMITIVE_STATE_LONG)) != 0;
- }
-
- public boolean isSubtypeOfInt() {
- return !isDouble();
- }
-
- // this method exists just for readability
- public Object getMaterializedObject() {
- return getDelegate();
- }
-
- // this method exists just for readability
- public void setMaterializedObject(Object materializedPrimitive) {
- setDelegate(materializedPrimitive);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
-
- CompilerAsserts.neverPartOfCompilation();
-
- PrimitiveNativeWrapper other = (PrimitiveNativeWrapper) obj;
- if (other.state == state && other.value == value && other.dvalue == dvalue) {
- // n.b.: in the equals, we also require the native pointer to be the same. The
- // reason for this is to avoid native pointer sharing. Handles are shared if the
- // objects are equal but in this case we must not share because otherwise we would
- // mess up the reference counts.
- return getNativePointer() == other.getNativePointer();
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return (Long.hashCode(value) ^ Long.hashCode(Double.doubleToRawLongBits(dvalue)) ^ state);
- }
-
- @Override
- public String toString() {
- String typeName;
- if (isIntLike()) {
- typeName = "int";
- } else if (isDouble()) {
- typeName = "float";
- } else if (isBool()) {
- typeName = "bool";
- } else {
- typeName = "unknown";
- }
- return "PrimitiveNativeWrapper(" + typeName + "(" + value + ")" + ')';
- }
-
- public static PrimitiveNativeWrapper createBool(boolean val) {
- return new PrimitiveNativeWrapper(PRIMITIVE_STATE_BOOL, PInt.intValue(val));
- }
-
- public static PrimitiveNativeWrapper createInt(int val) {
- return new PrimitiveNativeWrapper(PRIMITIVE_STATE_INT, val);
- }
-
- public static PrimitiveNativeWrapper createLong(long val) {
- return new PrimitiveNativeWrapper(PRIMITIVE_STATE_LONG, val);
- }
-
- public static PrimitiveNativeWrapper createDouble(double val) {
- return new PrimitiveNativeWrapper(val);
- }
-
- @ExportMessage
- @TruffleBoundary
- int identityHashCode() {
- int val = Byte.hashCode(state) ^ Long.hashCode(value);
- if (Double.isNaN(dvalue)) {
- return val;
- } else {
- return val ^ Double.hashCode(dvalue);
- }
- }
-
- @ExportMessage
- TriState isIdenticalOrUndefined(Object obj) {
- if (obj instanceof PrimitiveNativeWrapper) {
- /*
- * This basically emulates singletons for boxed values. However, we need to do so to
- * preserve the invariant that storing an object into a list and getting it out (in the
- * same critical region) returns the same object.
- */
- PrimitiveNativeWrapper other = (PrimitiveNativeWrapper) obj;
- if (other.state == state && other.value == value && (other.dvalue == dvalue || Double.isNaN(dvalue) && Double.isNaN(other.dvalue))) {
- /*
- * n.b.: in the equals, we also require the native pointer to be the same. The
- * reason for this is to avoid native pointer sharing. Handles are shared if the
- * objects are equal but in this case we must not share because otherwise we would
- * mess up the reference counts.
- */
- return TriState.valueOf(this.getNativePointer() == other.getNativePointer());
- }
- return TriState.FALSE;
- } else {
- return TriState.UNDEFINED;
- }
- }
-
- @ExportMessage
- abstract static class AsPointer {
-
- @Specialization(guards = {"obj.isBool()", "!obj.isNative()"})
- static long doBoolNotNative(PrimitiveNativeWrapper obj,
- @Bind Node inliningTarget,
- @Cached MaterializeDelegateNode materializeNode) {
- // special case for True and False singletons
- PInt boxed = (PInt) materializeNode.execute(inliningTarget, obj);
- assert obj.getNativePointer() == boxed.getNativeWrapper().getNativePointer();
- return obj.getNativePointer();
- }
-
- @Specialization(guards = {"!obj.isBool() || obj.isNative()"})
- static long doBoolNative(PrimitiveNativeWrapper obj) {
- return obj.getNativePointer();
- }
- }
-
- @ExportMessage
- boolean isPointer() {
- return isNative();
- }
-
- @ExportMessage
- void toNative(
- @Bind Node inliningTarget,
- @Cached CApiTransitions.FirstToNativeNode firstToNativeNode) {
- if (!isNative()) {
- boolean immortal = isBool();
- assert !isBool() || (PythonContext.get(inliningTarget).getCApiContext().getCachedBooleanPrimitiveNativeWrapper(value != 0) == this);
- setNativePointer(firstToNativeNode.execute(inliningTarget, this, immortal));
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java
index 1a86e605fa..b4c4af000a 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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,13 +41,19 @@
package com.oracle.graal.python.builtins.objects.cext.capi;
import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.checkThrowableBeforeNative;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import com.oracle.graal.python.annotations.Builtin;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeNode;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionToNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.function.PKeyword;
@@ -55,48 +61,59 @@
import com.oracle.graal.python.nodes.argument.CreateArgumentsNode;
import com.oracle.graal.python.nodes.argument.keywords.ExpandKeywordStarargsNode;
import com.oracle.graal.python.nodes.argument.positional.ExecutePositionalStarargsNode;
-import com.oracle.graal.python.nodes.call.CallDispatchers;
+import com.oracle.graal.python.nodes.call.CallDispatchers.SimpleIndirectInvokeNode;
import com.oracle.graal.python.runtime.GilNode;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.graal.python.runtime.nativeaccess.NativeSignature;
+import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType;
import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
-import com.oracle.truffle.api.interop.ArityException;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.interop.UnsupportedTypeException;
-import com.oracle.truffle.api.library.CachedLibrary;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
-import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.strings.TruffleString;
-import com.oracle.truffle.nfi.api.SignatureLibrary;
/**
* A wrapper class for managed functions such that they can be called with native function pointers
- * (like C type {@code PyCFunction}). This is very similar to {@link PyProcsWrapper} but the main
+ * (like C type {@code PyCFunction}). This is very similar to {@link TpSlotWrapper} but the main
* difference is that this wrapper does not keep a reference to the function object but only to the
* {@link RootCallTarget}
*
- * Since in C, function pointers are expected to valid the whole time, NFI closure must be kept
- * alive as long as the context lives. Referencing a function object like {@link PyProcsWrapper}
+ * Since in C, function pointers are expected to be valid the whole time, the native closure must be
+ * kept alive as long as the context lives. Referencing a function object like {@link TpSlotWrapper}
* does may therefore cause significant memory leaks.
*
*/
-@ExportLibrary(InteropLibrary.class)
-public abstract class PyCFunctionWrapper implements TruffleObject {
+public abstract class PyCFunctionWrapper {
+
+ private static final NativeSignature SIGNATURE_1_ARG = NativeSignature.create(NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER);
+ private static final NativeSignature SIGNATURE_2_ARG = NativeSignature.create(NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER);
+ private static final NativeSignature SIGNATURE_3_ARG = NativeSignature.create(NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER,
+ NativeSimpleType.RAW_POINTER);
+
+ private static final MethodHandle HANDLE_UNARY;
+ private static final MethodHandle HANDLE_BINARY;
+ private static final MethodHandle HANDLE_VARARGS;
+ private static final MethodHandle HANDLE_KEYWORDS;
+
+ static {
+ try {
+ HANDLE_UNARY = MethodHandles.lookup().findStatic(PyCFunctionUnaryWrapper.class, "executeUnary", MethodType.methodType(long.class, PyCFunctionUnaryWrapper.class, long.class));
+ HANDLE_BINARY = MethodHandles.lookup().findStatic(PyCFunctionBinaryWrapper.class, "executeBinary",
+ MethodType.methodType(long.class, PyCFunctionBinaryWrapper.class, long.class, long.class));
+ HANDLE_VARARGS = MethodHandles.lookup().findStatic(PyCFunctionVarargsWrapper.class, "executeVarargs",
+ MethodType.methodType(long.class, PyCFunctionVarargsWrapper.class, long.class, long.class));
+ HANDLE_KEYWORDS = MethodHandles.lookup().findStatic(PyCFunctionKeywordsWrapper.class, "executeKeywords",
+ MethodType.methodType(long.class, PyCFunctionKeywordsWrapper.class, long.class, long.class, long.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
protected final RootCallTarget callTarget;
protected final Signature signature;
protected final TruffleString callTargetName;
protected final CApiTiming timing;
- private long pointer;
+ private final long pointer;
/**
* Built-in functions may appear as {@link CExtContext#METH_VARARGS} etc. but we implement them
@@ -110,7 +127,8 @@ public abstract class PyCFunctionWrapper implements TruffleObject {
*/
protected final Object[] defaults;
- protected PyCFunctionWrapper(RootCallTarget callTarget, Signature signature, Object[] defaults) {
+ @SuppressWarnings("this-escape")
+ protected PyCFunctionWrapper(RootCallTarget callTarget, Signature signature, Object[] defaults, NativeSignature upcallSignature, MethodHandle methodHandle) {
assert callTarget != null;
assert signature != null;
this.callTarget = callTarget;
@@ -119,6 +137,8 @@ protected PyCFunctionWrapper(RootCallTarget callTarget, Signature signature, Obj
String ctName = callTarget.getRootNode().getName();
this.callTargetName = PythonUtils.toTruffleStringUncached(ctName);
this.timing = CApiTiming.create(false, ctName);
+ CApiContext cApiContext = PythonContext.get(null).getCApiContext();
+ this.pointer = cApiContext.registerClosure(getClass().getSimpleName(), upcallSignature, methodHandle.bindTo(this), this, getDelegate());
}
public final RootCallTarget getCallTarget() {
@@ -130,36 +150,7 @@ public final Object getDelegate() {
return callTarget;
}
- abstract String getSignature();
-
- @ExportMessage
- boolean isExecutable() {
- return true;
- }
-
- @ExportMessage
- @SuppressWarnings({"unused", "static-method"})
- protected Object execute(Object[] arguments) throws UnsupportedTypeException, ArityException, UnsupportedMessageException {
- throw CompilerDirectives.shouldNotReachHere("abstract class");
- }
-
- @ExportMessage
- @TruffleBoundary
- protected void toNative(
- @CachedLibrary(limit = "1") SignatureLibrary signatureLibrary) {
- if (pointer == 0) {
- CApiContext cApiContext = PythonContext.get(null).getCApiContext();
- pointer = cApiContext.registerClosure(getSignature(), this, getDelegate(), signatureLibrary);
- }
- }
-
- @ExportMessage
- protected boolean isPointer() {
- return pointer != 0;
- }
-
- @ExportMessage
- protected long asPointer() {
+ public final long getPointer() {
return pointer;
}
@@ -198,230 +189,143 @@ public static PyCFunctionWrapper createFromBuiltinFunction(CApiContext cApiConte
} else if (CExtContext.isMethVarargsWithKeywords(flags)) {
return cApiContext.getOrCreatePyCFunctionWrapper(ct, k -> new PyCFunctionKeywordsWrapper(k, signature, defaults));
} else {
- throw CompilerDirectives.shouldNotReachHere("other signature " + Integer.toHexString(flags));
+ throw shouldNotReachHere("other signature " + Integer.toHexString(flags));
}
}
- @ExportLibrary(InteropLibrary.class)
static final class PyCFunctionUnaryWrapper extends PyCFunctionWrapper {
PyCFunctionUnaryWrapper(RootCallTarget callTarget, Signature signature, Object[] defaults) {
- super(callTarget, signature, defaults);
+ super(callTarget, signature, defaults, SIGNATURE_1_ARG, HANDLE_UNARY);
}
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CreateArgumentsNode createArgsNode,
- @Cached CallDispatchers.CallTargetCachedInvokeNode invokeNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- /*
- * Accept a second argument here, since these functions are sometimes called using
- * METH_O with a "NULL" value.
- */
- if (arguments.length > 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(1, 2, arguments.length);
- }
+ @SuppressWarnings("try")
+ private static long executeUnary(PyCFunctionUnaryWrapper self, long arg0) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
try {
- Object result;
- Object jArg0 = toJavaNode.execute(arguments[0]);
- Object[] pArgs = createArgsNode.execute(inliningTarget, callTargetName, PythonUtils.EMPTY_OBJECT_ARRAY, PKeyword.EMPTY_KEYWORDS, signature, jArg0, null,
- defaults, PKeyword.EMPTY_KEYWORDS, false);
- result = invokeNode.execute(null, inliningTarget, callTarget, pArgs);
- return toNativeNode.execute(result);
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object[] pArgs = CreateArgumentsNode.executeUncached(self.callTargetName, PythonUtils.EMPTY_OBJECT_ARRAY, PKeyword.EMPTY_KEYWORDS, self.signature, jArg0, null,
+ self.defaults, PKeyword.EMPTY_KEYWORDS, false);
+ Object result = SimpleIndirectInvokeNode.executeUncached(self.callTarget, pArgs);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
} catch (Throwable t) {
- throw checkThrowableBeforeNative(t, toString(), "");
+ throw checkThrowableBeforeNative(t, self.toString(), "");
}
} catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
} finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
+ CApiTiming.exit(self.timing);
}
}
- @Override
- protected String getSignature() {
- return "(POINTER):POINTER";
- }
-
@Override
protected String getFlagsRepr() {
return "METH_NOARGS";
}
}
- @ExportLibrary(InteropLibrary.class)
static final class PyCFunctionBinaryWrapper extends PyCFunctionWrapper {
PyCFunctionBinaryWrapper(RootCallTarget callTarget, Signature signature, Object[] defaults) {
- super(callTarget, signature, defaults);
+ super(callTarget, signature, defaults, SIGNATURE_2_ARG, HANDLE_BINARY);
}
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallDispatchers.CallTargetCachedInvokeNode invokeNode,
- @Cached CreateArgumentsNode createArgsNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(2, 2, arguments.length);
- }
+ @SuppressWarnings("try")
+ private static long executeBinary(PyCFunctionBinaryWrapper self, long arg0, long arg1) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
try {
- Object result;
- Object jArg0 = toJavaNode.execute(arguments[0]);
- Object jArg1 = toJavaNode.execute(arguments[1]);
- Object[] pArgs = createArgsNode.execute(inliningTarget, callTargetName, new Object[]{jArg1}, PKeyword.EMPTY_KEYWORDS, signature, jArg0, null,
- defaults, PKeyword.EMPTY_KEYWORDS, false);
- result = invokeNode.execute(null, inliningTarget, callTarget, pArgs);
- return toNativeNode.execute(result);
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object[] pArgs = CreateArgumentsNode.executeUncached(self.callTargetName, new Object[]{jArg1}, PKeyword.EMPTY_KEYWORDS, self.signature, jArg0, null,
+ self.defaults, PKeyword.EMPTY_KEYWORDS, false);
+ Object result = SimpleIndirectInvokeNode.executeUncached(self.callTarget, pArgs);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
} catch (Throwable t) {
- throw checkThrowableBeforeNative(t, toString(), "");
+ throw checkThrowableBeforeNative(t, self.toString(), "");
}
} catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
} finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
+ CApiTiming.exit(self.timing);
}
}
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER):POINTER";
- }
-
@Override
protected String getFlagsRepr() {
return "METH_O";
}
}
- @ExportLibrary(InteropLibrary.class)
static final class PyCFunctionVarargsWrapper extends PyCFunctionWrapper {
PyCFunctionVarargsWrapper(RootCallTarget callTarget, Signature signature, Object[] defaults) {
- super(callTarget, signature, defaults);
+ super(callTarget, signature, defaults, SIGNATURE_2_ARG, HANDLE_VARARGS);
}
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached ExecutePositionalStarargsNode posStarargsNode,
- @Cached CreateArgumentsNode createArgsNode,
- @Cached CallDispatchers.CallTargetCachedInvokeNode invokeNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(2, 2, arguments.length);
- }
+ @SuppressWarnings("try")
+ private static long executeVarargs(PyCFunctionVarargsWrapper self, long arg0, long arg1) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
try {
- Object result;
- Object receiver = toJavaNode.execute(arguments[0]);
- Object starArgs = toJavaNode.execute(arguments[1]);
- Object[] starArgsArray = posStarargsNode.executeWith(null, starArgs);
- Object[] pArgs = createArgsNode.execute(inliningTarget, callTargetName, starArgsArray, PKeyword.EMPTY_KEYWORDS, signature, receiver, null,
- defaults, PKeyword.EMPTY_KEYWORDS, false);
- result = invokeNode.execute(null, inliningTarget, callTarget, pArgs);
- return toNativeNode.execute(result);
+ Object receiver = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object starArgs = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object[] starArgsArray = ExecutePositionalStarargsNode.executeUncached(starArgs);
+ Object[] pArgs = CreateArgumentsNode.executeUncached(self.callTargetName, starArgsArray, PKeyword.EMPTY_KEYWORDS, self.signature, receiver, null,
+ self.defaults, PKeyword.EMPTY_KEYWORDS, false);
+ Object result = SimpleIndirectInvokeNode.executeUncached(self.callTarget, pArgs);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
} catch (Throwable t) {
- throw checkThrowableBeforeNative(t, toString(), "");
+ throw checkThrowableBeforeNative(t, self.toString(), "");
}
} catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
} finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
+ CApiTiming.exit(self.timing);
}
}
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER):POINTER";
- }
-
@Override
protected String getFlagsRepr() {
return "METH_VARARGS";
}
}
- @ExportLibrary(InteropLibrary.class)
static final class PyCFunctionKeywordsWrapper extends PyCFunctionWrapper {
PyCFunctionKeywordsWrapper(RootCallTarget callTarget, Signature signature, Object[] defaults) {
- super(callTarget, signature, defaults);
+ super(callTarget, signature, defaults, SIGNATURE_3_ARG, HANDLE_KEYWORDS);
}
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached ExecutePositionalStarargsNode posStarargsNode,
- @Cached CreateArgumentsNode createArgsNode,
- @Cached CallDispatchers.CallTargetCachedInvokeNode invokeNode,
- @Cached ExpandKeywordStarargsNode expandKwargsNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 3) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, 3, arguments.length);
- }
+ @SuppressWarnings("try")
+ private static long executeKeywords(PyCFunctionKeywordsWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
try {
- Object receiver = toJavaNode.execute(arguments[0]);
- Object starArgs = toJavaNode.execute(arguments[1]);
- Object kwArgs = toJavaNode.execute(arguments[2]);
-
- Object[] starArgsArray = posStarargsNode.executeWith(null, starArgs);
- PKeyword[] kwArgsArray = expandKwargsNode.execute(inliningTarget, kwArgs);
- Object[] pArgs = createArgsNode.execute(inliningTarget, callTargetName, starArgsArray, kwArgsArray, signature, receiver, null,
- defaults, PKeyword.EMPTY_KEYWORDS, false);
- Object result = invokeNode.execute(null, inliningTarget, callTarget, pArgs);
- return toNativeNode.execute(result);
+ Object receiver = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object starArgs = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object kwArgs = NativeToPythonInternalNode.executeUncached(arg2, false);
+ Object[] starArgsArray = ExecutePositionalStarargsNode.executeUncached(starArgs);
+ PKeyword[] kwArgsArray = ExpandKeywordStarargsNode.getUncached().execute(null, kwArgs);
+ Object[] pArgs = CreateArgumentsNode.executeUncached(self.callTargetName, starArgsArray, kwArgsArray, self.signature, receiver, null,
+ self.defaults, PKeyword.EMPTY_KEYWORDS, false);
+ Object result = SimpleIndirectInvokeNode.executeUncached(self.callTarget, pArgs);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
} catch (Throwable t) {
- throw checkThrowableBeforeNative(t, toString(), "");
+ throw checkThrowableBeforeNative(t, self.toString(), "");
}
} catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
} finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
+ CApiTiming.exit(self.timing);
}
}
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):POINTER";
- }
-
@Override
protected String getFlagsRepr() {
return "METH_KEYWORDS";
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyDateTimeCAPIWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyDateTimeCAPIWrapper.java
index 0ae04588bd..6805942e6c 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyDateTimeCAPIWrapper.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyDateTimeCAPIWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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,19 +41,19 @@
package com.oracle.graal.python.builtins.objects.cext.capi;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_INIT_NATIVE_DATETIME;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.allocate;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writePtrField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.free;
import static com.oracle.graal.python.nodes.StringLiterals.T_DATE;
import static com.oracle.graal.python.nodes.StringLiterals.T_DATETIME;
import static com.oracle.graal.python.nodes.StringLiterals.T_TIME;
+import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL;
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltinRegistry;
import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNewRefNodeGen;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
import com.oracle.graal.python.builtins.objects.type.TypeNodes.SetBasicSizeNode;
@@ -63,7 +63,9 @@
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleString.Encoding;
/**
* A class to allocate and initialize C structure {@code PyDateTime_CAPI}.
@@ -96,7 +98,7 @@
public abstract class PyDateTimeCAPIWrapper {
static final TruffleString T_DATETIME_CAPI = tsLiteral("datetime_CAPI");
- static final byte[] T_PYDATETIME_CAPSULE_NAME = PyCapsule.capsuleName("datetime.datetime_CAPI");
+ static final String J_PYDATETIME_CAPSULE_NAME = "datetime.datetime_CAPI";
private static final TruffleString T_TIMEDELTA = tsLiteral("timedelta");
public static final TruffleString T_TZINFO = tsLiteral("tzinfo");
@@ -121,17 +123,21 @@ private PyDateTimeCAPIWrapper() {
public static PyCapsule initWrapper(PythonContext context, CApiContext capiContext) {
CompilerAsserts.neverPartOfCompilation();
- PCallCapiFunction callCapiFunction = PCallCapiFunction.getUncached();
- callCapiFunction.call(FUN_INIT_NATIVE_DATETIME);
+ try {
+ ExternalFunctionInvoker.invokeINIT_NATIVE_DATETIME(CApiContext.getNativeSymbol(null, FUN_INIT_NATIVE_DATETIME).getAddress());
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
Object datetimeModule = AbstractImportNode.importModule(T_DATETIME);
capiContext.timezoneType = PyObjectGetAttr.executeUncached(datetimeModule, T_TIMEZONE);
- Object pointerObject = allocatePyDatetimeCAPI(datetimeModule);
+ long pointer = allocatePyDatetimeCAPI(datetimeModule);
- PyCapsule capsule = PFactory.createCapsuleJavaName(context.getLanguage(), pointerObject, T_PYDATETIME_CAPSULE_NAME);
+ long name = context.stringToNativeUtf8Bytes(TruffleString.fromJavaStringUncached(J_PYDATETIME_CAPSULE_NAME, Encoding.US_ASCII), true);
+ PyCapsule capsule = PFactory.createCapsuleNativeName(context.getLanguage(), pointer, name);
PyObjectSetAttr.executeUncached(datetimeModule, T_DATETIME_CAPI, capsule);
- assert PyObjectGetAttr.executeUncached(datetimeModule, T_DATETIME_CAPI) != context.getNativeNull();
+ assert PyObjectGetAttr.executeUncached(datetimeModule, T_DATETIME_CAPI) != NATIVE_NULL;
return capsule;
}
@@ -142,52 +148,50 @@ public static PyCapsule initWrapper(PythonContext context, CApiContext capiConte
*/
public static void destroyWrapper(PyCapsule capsule) {
CompilerAsserts.neverPartOfCompilation();
- CStructAccess.FreeNode.executeUncached(capsule.getPointer());
+ free(capsule.getPointer());
}
- private static Object allocatePyDatetimeCAPI(Object datetimeModule) {
- CStructAccess.AllocateNode allocNode = CStructAccessFactory.AllocateNodeGen.getUncached();
- CStructAccess.WritePointerNode writePointerNode = CStructAccessFactory.WritePointerNodeGen.getUncached();
-
+ private static long allocatePyDatetimeCAPI(Object datetimeModule) {
PyObjectGetAttr getAttr = PyObjectGetAttr.getUncached();
- PythonToNativeNewRefNode toNativeNode = PythonToNativeNewRefNodeGen.getUncached();
+ PythonToNativeNewRefNode toNativeNode = PythonToNativeNewRefNode.getUncached();
PythonManagedClass date = (PythonManagedClass) getAttr.execute(null, datetimeModule, T_DATE);
SetBasicSizeNode.executeUncached(date, CStructs.PyDateTime_Date.size());
- Object dateType = toNativeNode.execute(date);
+ long dateType = toNativeNode.executeLong(date);
PythonManagedClass dt = (PythonManagedClass) getAttr.execute(null, datetimeModule, T_DATETIME);
SetBasicSizeNode.executeUncached(dt, CStructs.PyDateTime_DateTime.size());
- Object datetimeType = toNativeNode.execute(dt);
+ long datetimeType = toNativeNode.executeLong(dt);
PythonManagedClass time = (PythonManagedClass) getAttr.execute(null, datetimeModule, T_TIME);
SetBasicSizeNode.executeUncached(time, CStructs.PyDateTime_Time.size());
- Object timeType = toNativeNode.execute(time);
+ long timeType = toNativeNode.executeLong(time);
PythonManagedClass delta = (PythonManagedClass) getAttr.execute(null, datetimeModule, T_TIMEDELTA);
SetBasicSizeNode.executeUncached(delta, CStructs.PyDateTime_Delta.size());
- Object deltaType = toNativeNode.execute(delta);
+ long deltaType = toNativeNode.executeLong(delta);
- Object tzInfoType = toNativeNode.execute(getAttr.execute(null, datetimeModule, T_TZINFO));
+ long tzInfoType = toNativeNode.executeLong(getAttr.execute(null, datetimeModule, T_TZINFO));
Object timezoneType = getAttr.execute(null, datetimeModule, T_TIMEZONE);
- Object timezoneUTC = toNativeNode.execute(getAttr.execute(null, timezoneType, T_UTC));
-
- Object mem = allocNode.alloc(CStructs.PyDateTime_CAPI);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__DateType, dateType);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__DateTimeType, datetimeType);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__TimeType, timeType);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__DeltaType, deltaType);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__TZInfoType, tzInfoType);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__TimeZone_UTC, timezoneUTC);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__Date_FromDate, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Date_FromDate);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__DateTime_FromDateAndTime, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_DateTime_FromDateAndTime);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__Time_FromTime, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Time_FromTime);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__Delta_FromDelta, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Delta_FromDelta);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__TimeZone_FromTimeZone, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_TimeZone_FromTimeZone);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__DateTime_FromTimestamp, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_DateTime_FromTimestamp);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__Date_FromTimestamp, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Date_FromTimestamp);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__DateTime_FromDateAndTimeAndFold, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_DateTime_FromDateAndTimeAndFold);
- writePointerNode.write(mem, CFields.PyDateTime_CAPI__Time_FromTimeAndFold, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Time_FromTimeAndFold);
+ long timezoneUTC = toNativeNode.executeLong(getAttr.execute(null, timezoneType, T_UTC));
+
+ long mem = allocate(CStructs.PyDateTime_CAPI);
+ writePtrField(mem, CFields.PyDateTime_CAPI__DateType, dateType);
+ writePtrField(mem, CFields.PyDateTime_CAPI__DateTimeType, datetimeType);
+ writePtrField(mem, CFields.PyDateTime_CAPI__TimeType, timeType);
+ writePtrField(mem, CFields.PyDateTime_CAPI__DeltaType, deltaType);
+ writePtrField(mem, CFields.PyDateTime_CAPI__TZInfoType, tzInfoType);
+ writePtrField(mem, CFields.PyDateTime_CAPI__TimeZone_UTC, timezoneUTC);
+ writePtrField(mem, CFields.PyDateTime_CAPI__Date_FromDate, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Date_FromDate.getNativePointer());
+ writePtrField(mem, CFields.PyDateTime_CAPI__DateTime_FromDateAndTime, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_DateTime_FromDateAndTime.getNativePointer());
+ writePtrField(mem, CFields.PyDateTime_CAPI__Time_FromTime, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Time_FromTime.getNativePointer());
+ writePtrField(mem, CFields.PyDateTime_CAPI__Delta_FromDelta, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Delta_FromDelta.getNativePointer());
+ writePtrField(mem, CFields.PyDateTime_CAPI__TimeZone_FromTimeZone, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_TimeZone_FromTimeZone.getNativePointer());
+ writePtrField(mem, CFields.PyDateTime_CAPI__DateTime_FromTimestamp, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_DateTime_FromTimestamp.getNativePointer());
+ writePtrField(mem, CFields.PyDateTime_CAPI__Date_FromTimestamp, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Date_FromTimestamp.getNativePointer());
+ writePtrField(mem, CFields.PyDateTime_CAPI__DateTime_FromDateAndTimeAndFold,
+ PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_DateTime_FromDateAndTimeAndFold.getNativePointer());
+ writePtrField(mem, CFields.PyDateTime_CAPI__Time_FromTimeAndFold, PythonCextBuiltinRegistry.GraalPyPrivate_DateTimeCAPI_Time_FromTimeAndFold.getNativePointer());
return mem;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyErrStackItem.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyErrStackItem.java
deleted file mode 100644
index cade157aea..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyErrStackItem.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonStructNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes;
-import com.oracle.graal.python.nodes.object.GetClassNode;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.profiles.InlinedConditionProfile;
-
-/**
- * Emulates {@code _PyErr_StackItem}.
- */
-@ExportLibrary(InteropLibrary.class)
-public final class PyErrStackItem extends PythonStructNativeWrapper {
-
- public static final String J_EXC_TYPE = "exc_type";
- public static final String J_EXC_VALUE = "exc_value";
- public static final String J_EXC_TRACEBACK = "exc_traceback";
- public static final String J_PREVIOUS_ITEM = "previous_item";
-
- private final Object exception;
-
- public PyErrStackItem(Object exception) {
- this.exception = exception;
- }
-
- @ExportMessage
- @SuppressWarnings("static-method")
- boolean hasMembers() {
- return true;
- }
-
- @ExportMessage
- @SuppressWarnings("static-method")
- Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
- return new PythonAbstractObject.Keys(new Object[]{J_EXC_TYPE, J_EXC_VALUE, J_EXC_TRACEBACK, J_PREVIOUS_ITEM});
- }
-
- @ExportMessage
- @SuppressWarnings("static-method")
- boolean isMemberReadable(String key) {
- return J_EXC_TYPE.equals(key) || J_EXC_VALUE.equals(key) || J_EXC_TRACEBACK.equals(key) || J_PREVIOUS_ITEM.equals(key);
- }
-
- @ExportMessage
- Object readMember(String key,
- @Bind Node inliningTarget,
- @Cached GetClassNode getClassNode,
- @Cached ExceptionNodes.GetTracebackNode getTracebackNode,
- @Cached PythonToNativeNode toSulongNode) {
- Object result = null;
- if (exception != null) {
- switch (key) {
- case J_EXC_TYPE -> result = getClassNode.execute(inliningTarget, exception);
- case J_EXC_VALUE -> result = exception;
- case J_EXC_TRACEBACK -> {
- result = getTracebackNode.execute(inliningTarget, exception);
- if (result == PNone.NONE) {
- result = null;
- }
- }
- }
- }
- if (result == null) {
- result = PythonContext.get(toSulongNode).getNativeNull();
- }
- return toSulongNode.execute(result);
- }
-
- @ExportMessage
- boolean isPointer() {
- return isNative();
- }
-
- @ExportMessage
- public long asPointer() throws UnsupportedMessageException {
- if (isNative()) {
- return getNativePointer();
- }
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw UnsupportedMessageException.create();
- }
-
- @ExportMessage
- void toNative(@Bind Node inliningTarget,
- @Cached InlinedConditionProfile isNativeProfile) {
- if (!isNative(inliningTarget, isNativeProfile)) {
- // TODO(fa): not yet implemented
- throw CompilerDirectives.shouldNotReachHere();
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMemoryViewWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMemoryViewWrapper.java
index 97e3d0e450..8e2ae4304b 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMemoryViewWrapper.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMemoryViewWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -40,22 +40,23 @@
*/
package com.oracle.graal.python.builtins.objects.cext.capi;
-import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PTR_ADD;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMemoryViewObject__exports;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMemoryViewObject__flags;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_refcnt;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_type;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.getFieldPtr;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writePtrField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.calloc;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.mallocLongArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeLongArrayElement;
import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
-import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.AllocateNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.GetElementPtrNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView;
@@ -63,111 +64,77 @@
import com.oracle.graal.python.builtins.objects.type.TypeFlags;
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetTypeFlagsNode;
import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode;
-import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
/**
* Wrapper object for {@code PMemoryView}.
*/
-public final class PyMemoryViewWrapper extends PythonAbstractObjectNativeWrapper {
- private NativePointer replacement;
+public abstract class PyMemoryViewWrapper {
- public PyMemoryViewWrapper(PythonObject delegate) {
- super(delegate);
- assert delegate instanceof PMemoryView;
+ private PyMemoryViewWrapper() {
}
- private static Object intArrayToNativePySSizeArray(int[] intArray) {
- Object mem = CStructAccess.AllocateNode.allocUncached(intArray.length * Long.BYTES);
- CStructAccess.WriteLongNode.getUncached().writeIntArray(mem, intArray);
+ private static long intArrayToNativePySSizeArray(int[] intArray) {
+ long mem = mallocLongArray(intArray.length);
+ for (int i = 0; i < intArray.length; i++) {
+ writeLongArrayElement(mem, i, intArray[i]);
+ }
return mem;
}
@TruffleBoundary
- private static long allocate(PMemoryView object) {
- GetElementPtrNode getElementNode = GetElementPtrNode.getUncached();
- CStructAccess.WritePointerNode writePointerNode = CStructAccess.WritePointerNode.getUncached();
- CStructAccess.WriteLongNode writeI64Node = CStructAccess.WriteLongNode.getUncached();
- CStructAccess.WriteIntNode writeI32Node = CStructAccess.WriteIntNode.getUncached();
+ public static long allocate(PMemoryView object) {
CExtNodes.AsCharPointerNode asCharPointerNode = CExtNodes.AsCharPointerNode.getUncached();
Object type = GetPythonObjectClassNode.executeUncached(object);
boolean gc = (GetTypeFlagsNode.executeUncached(type) & TypeFlags.HAVE_GC) != 0;
long presize = gc ? CStructs.PyGC_Head.size() : 0;
- long memWithHead = PythonUtils.coerceToLong(AllocateNode.allocUncached(CStructs.PyMemoryViewObject.size() + presize), InteropLibrary.getUncached());
+ long memWithHead = calloc(CStructs.PyMemoryViewObject.size() + presize);
long mem = memWithHead + presize;
- writePointerNode.write(mem, PyObject__ob_type, PythonToNativeNewRefNode.executeUncached(type));
- writeI64Node.write(mem, PyObject__ob_refcnt, PythonAbstractObjectNativeWrapper.IMMORTAL_REFCNT);
- writeI32Node.write(mem, PyMemoryViewObject__flags, object.getFlags());
- writeI64Node.write(mem, PyMemoryViewObject__exports, object.getExports().get());
+ writePtrField(mem, PyObject__ob_type, PythonToNativeNewRefNode.executeLongUncached(type));
+ writeLongField(mem, PyObject__ob_refcnt, PythonObject.IMMORTAL_REFCNT);
+ writeIntField(mem, PyMemoryViewObject__flags, object.getFlags());
+ writeLongField(mem, PyMemoryViewObject__exports, object.getExports().get());
// TODO: ignoring mbuf, hash and weakreflist for now
- Object view = getElementNode.getElementPtr(mem, CFields.PyMemoryViewObject__view);
+ long view = getFieldPtr(mem, CFields.PyMemoryViewObject__view);
if (object.getBuffer() != null) {
- Object buf = object.getBufferPointer();
- if (buf == null) {
+ long buf = object.getBufferPointer();
+ if (buf == NULLPTR) {
buf = PythonBufferAccessLibrary.getUncached().getNativePointer(object.getBuffer());
- if (buf == null) {
+ if (buf == NULLPTR) {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw shouldNotReachHere("Cannot convert managed object to native storage: " + object.getBuffer().getClass().getSimpleName());
}
}
if (object.getOffset() != 0) {
- if (buf instanceof Long ptr) {
- buf = ptr + object.getOffset();
- } else {
- InteropLibrary ptrLib = InteropLibrary.getUncached(buf);
- if (ptrLib.isPointer(buf)) {
- try {
- buf = ptrLib.asPointer(buf) + object.getOffset();
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- } else {
- buf = CExtNodes.PCallCapiFunction.callUncached(FUN_PTR_ADD, buf, (long) object.getOffset());
- }
- }
+ buf = buf + object.getOffset();
}
- writePointerNode.write(view, CFields.Py_buffer__buf, buf);
+ writePtrField(view, CFields.Py_buffer__buf, buf);
}
if (object.getOwner() != null) {
- writePointerNode.write(view, CFields.Py_buffer__obj, PythonToNativeNewRefNode.executeUncached(object.getOwner()));
+ writePtrField(view, CFields.Py_buffer__obj, PythonToNativeNewRefNode.executeLongUncached(object.getOwner()));
}
- writeI64Node.write(view, CFields.Py_buffer__len, object.getLength());
- writeI64Node.write(view, CFields.Py_buffer__itemsize, object.getItemSize());
- writeI32Node.write(view, CFields.Py_buffer__readonly, PInt.intValue(object.isReadOnly()));
- writeI32Node.write(view, CFields.Py_buffer__ndim, object.getDimensions());
+ writeLongField(view, CFields.Py_buffer__len, object.getLength());
+ writeLongField(view, CFields.Py_buffer__itemsize, object.getItemSize());
+ writeIntField(view, CFields.Py_buffer__readonly, PInt.intValue(object.isReadOnly()));
+ writeIntField(view, CFields.Py_buffer__ndim, object.getDimensions());
if (object.getFormatString() != null) {
- writePointerNode.write(view, CFields.Py_buffer__format, asCharPointerNode.execute(object.getFormatString()));
+ writePtrField(view, CFields.Py_buffer__format, asCharPointerNode.execute(object.getFormatString()));
}
if (object.getBufferShape() != null) {
- writePointerNode.write(view, CFields.Py_buffer__shape, intArrayToNativePySSizeArray(object.getBufferShape()));
+ writePtrField(view, CFields.Py_buffer__shape, intArrayToNativePySSizeArray(object.getBufferShape()));
}
if (object.getBufferStrides() != null) {
- writePointerNode.write(view, CFields.Py_buffer__strides, intArrayToNativePySSizeArray(object.getBufferStrides()));
+ writePtrField(view, CFields.Py_buffer__strides, intArrayToNativePySSizeArray(object.getBufferStrides()));
}
if (object.getBufferSuboffsets() != null) {
- writePointerNode.write(view, CFields.Py_buffer__suboffsets, intArrayToNativePySSizeArray(object.getBufferSuboffsets()));
+ writePtrField(view, CFields.Py_buffer__suboffsets, intArrayToNativePySSizeArray(object.getBufferSuboffsets()));
}
return mem;
}
-
- public Object getReplacement() {
- if (replacement == null) {
- long ptr = allocate((PMemoryView) getDelegate());
- // TODO: need to convert to interop pointer for NFI for now
- replacement = new NativePointer(ptr);
- // TODO: this passes "false" for allocatedFromJava, although it actually is. The
- // problem, however, is that this struct contains nested allocations from Java. This
- // needs to be cleaned up...
- CApiTransitions.createReference(this, ptr, false);
- }
- return replacement;
- }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMethodDefHelper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMethodDefHelper.java
index d42a701ea4..1b5797f261 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMethodDefHelper.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyMethodDefHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -44,21 +44,20 @@
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMethodDef__ml_flags;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMethodDef__ml_meth;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMethodDef__ml_name;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import java.util.logging.Level;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.FromCharPointerNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CStringWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.FreeNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.function.PKeyword;
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.HiddenAttr;
-import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.SpecialAttributeNames;
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
@@ -69,8 +68,6 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLogger;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.strings.TruffleString;
/**
@@ -86,7 +83,7 @@
* {@code PyMethodDef} we *MUST NOT* keep a reference to any runtime objects because they would
* leak.
*
- *
+ *
* @param name The name of the function or method object.
* @param meth A reference to the executable function. In CPython, this is a function pointer of
* type {@code PyCFunction}. In our case, it can either be a native function pointer or a
@@ -98,10 +95,15 @@ public record PyMethodDefHelper(TruffleString name, Object meth, int flags, Truf
private static final TruffleLogger LOGGER = CApiContext.getLogger(PyMethodDefHelper.class);
+ public PyMethodDefHelper {
+ assert meth instanceof NativeFunctionPointer || meth instanceof PyCFunctionWrapper;
+ }
+
private static Object getMethFromBuiltinFunction(CApiContext cApiContext, PBuiltinFunction object) {
PKeyword[] kwDefaults = object.getKwDefaults();
for (int i = 0; i < kwDefaults.length; i++) {
if (ExternalFunctionNodes.KW_CALLABLE.equals(kwDefaults[i].getName())) {
+ assert kwDefaults[i].getValue() instanceof NativeFunctionPointer;
// This can happen for slot wrapper methods of native slots
return kwDefaults[i].getValue();
}
@@ -110,7 +112,7 @@ private static Object getMethFromBuiltinFunction(CApiContext cApiContext, PBuilt
}
@TruffleBoundary
- public static Object create(CApiContext cApiContext, PBuiltinFunction builtinFunction) {
+ public static long create(CApiContext cApiContext, PBuiltinFunction builtinFunction) {
Object docObj = builtinFunction.getAttribute(SpecialAttributeNames.T___DOC__);
TruffleString doc;
if (docObj instanceof PNone) {
@@ -123,16 +125,12 @@ public static Object create(CApiContext cApiContext, PBuiltinFunction builtinFun
}
}
PyMethodDefHelper pyMethodDef = new PyMethodDefHelper(builtinFunction.getName(), getMethFromBuiltinFunction(cApiContext, builtinFunction), builtinFunction.getFlags(), doc);
- Object result = cApiContext.getOrAllocateNativePyMethodDef(pyMethodDef);
+ long result = cApiContext.getOrAllocateNativePyMethodDef(pyMethodDef);
// store the PyMethodDef pointer to the built-in function object for fast access
- HiddenAttr.WriteNode.executeUncached(builtinFunction, HiddenAttr.METHOD_DEF_PTR, result);
+ HiddenAttr.WriteLongNode.executeUncached(builtinFunction, HiddenAttr.METHOD_DEF_PTR, result);
return result;
}
- public static Object create(CApiContext cApiContext, PBuiltinMethod builtinMethod) {
- return create(cApiContext, builtinMethod.getBuiltinFunction());
- }
-
/**
* Allocates a native {@code PyMethodDef} struct and initializes it.
*
@@ -148,86 +146,44 @@ public static Object create(CApiContext cApiContext, PBuiltinMethod builtinMetho
* @return The pointer object of the allocated struct.
*/
@TruffleBoundary
- Object allocate() {
- CStructAccess.AllocateNode allocNode = CStructAccessFactory.AllocateNodeGen.getUncached();
- CStructAccess.WritePointerNode writePointerNode = CStructAccessFactory.WritePointerNodeGen.getUncached();
- CStructAccess.WriteIntNode writeIntNode = CStructAccessFactory.WriteIntNodeGen.getUncached();
-
+ long allocate() {
assert name != null;
- CStringWrapper nameWrapper;
- try {
- nameWrapper = new CStringWrapper(name.switchEncodingUncached(TruffleString.Encoding.UTF_8), TruffleString.Encoding.UTF_8);
- } catch (CannotCastException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
+ PythonContext pythonContext = PythonContext.get(null);
+ long nativeName = pythonContext.stringToNativeUtf8Bytes(name, false);
+ long nativeDoc = doc != null ? pythonContext.stringToNativeUtf8Bytes(doc, false) : 0L;
+ long nativeMeth = meth instanceof NativeFunctionPointer f ? f.getAddress() : ((PyCFunctionWrapper) meth).getPointer();
- Object docWrapper;
- if (doc == null) {
- docWrapper = PythonContext.get(null).getNativeNull();
- } else {
- try {
- docWrapper = new CStringWrapper(doc.switchEncodingUncached(TruffleString.Encoding.UTF_8), TruffleString.Encoding.UTF_8);
- } catch (CannotCastException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
- Object mem = allocNode.alloc(CStructs.PyMethodDef);
- writePointerNode.write(mem, PyMethodDef__ml_name, nameWrapper);
- writePointerNode.write(mem, PyMethodDef__ml_meth, meth);
- writeIntNode.write(mem, PyMethodDef__ml_flags, flags);
- writePointerNode.write(mem, PyMethodDef__ml_doc, docWrapper);
+ long mem = CStructAccess.allocate(CStructs.PyMethodDef);
+ CStructAccess.writePtrField(mem, PyMethodDef__ml_name, nativeName);
+ CStructAccess.writePtrField(mem, PyMethodDef__ml_meth, nativeMeth);
+ CStructAccess.writeIntField(mem, PyMethodDef__ml_flags, flags);
+ CStructAccess.writePtrField(mem, PyMethodDef__ml_doc, nativeDoc);
LOGGER.fine(() -> String.format("Allocated PyMethodDef(%s, %s, %d, %s) at %s", name, meth, flags, doc, PythonUtils.formatPointer(mem)));
return mem;
}
- static void free(Object pointer) {
+ static void free(long pointer) {
CompilerAsserts.neverPartOfCompilation();
- CStructAccess.ReadPointerNode readPointerNode = CStructAccess.ReadPointerNode.getUncached();
-
LOGGER.fine(() -> "Freeing PyMethodDef at " + PythonUtils.formatPointer(pointer));
- Object namePointer = readPointerNode.read(pointer, PyMethodDef__ml_name);
- Object docPointer = readPointerNode.read(pointer, PyMethodDef__ml_doc);
+ long namePointer = CStructAccess.readPtrField(pointer, PyMethodDef__ml_name);
+ long docPointer = CStructAccess.readPtrField(pointer, PyMethodDef__ml_doc);
// we only read the other fields and decode strings for logging
if (LOGGER.isLoggable(Level.FINER)) {
- CStructAccess.ReadI32Node readIntNode = CStructAccessFactory.ReadI32NodeGen.getUncached();
- assert !PGuards.isNullOrZero(namePointer, InteropLibrary.getUncached());
- TruffleString name = FromCharPointerNodeGen.getUncached().execute(namePointer, false);
+ assert namePointer != NULLPTR;
+ TruffleString name = FromCharPointerNode.executeUncached(namePointer, false);
TruffleString doc = null;
- if (PGuards.isNullOrZero(namePointer, InteropLibrary.getUncached())) {
- doc = FromCharPointerNodeGen.getUncached().execute(docPointer, false);
- }
- int flags = readIntNode.read(pointer, PyMethodDef__ml_flags);
- Object methPointer = readPointerNode.read(pointer, PyMethodDef__ml_meth);
- Object meth;
- InteropLibrary methPointerLib = InteropLibrary.getUncached(methPointer);
- if (methPointerLib.isPointer(methPointer)) {
- try {
- meth = PythonContext.get(null).getCApiContext().getClosureDelegate(methPointerLib.asPointer(methPointer));
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- } else {
- // managed case: it is still the wrapper
- assert methPointer instanceof PythonNativeWrapper;
- meth = methPointer;
+ if (docPointer != NULLPTR) {
+ doc = FromCharPointerNode.executeUncached(docPointer, false);
}
+ int flags = CStructAccess.readIntField(pointer, PyMethodDef__ml_flags);
+ long methPointer = CStructAccess.readPtrField(pointer, PyMethodDef__ml_meth);
+ Object meth = PythonContext.get(null).getCApiContext().getClosureDelegate(methPointer);
LOGGER.finer(String.format("PyMethodDef(%s, %s, %d, %s) at %s freed.", name, meth, flags, doc, PythonUtils.formatPointer(pointer)));
}
- // managed case: 'const char *' is represented by CStringWrapper
- if (namePointer instanceof CStringWrapper nameWrapper) {
- nameWrapper.free();
- } else {
- FreeNode.executeUncached(namePointer);
- }
- if (docPointer instanceof CStringWrapper docWrapper) {
- docWrapper.free();
- } else {
- if (PGuards.isNullOrZero(docPointer, InteropLibrary.getUncached())) {
- FreeNode.executeUncached(docPointer);
- }
- }
- FreeNode.executeUncached(pointer);
+ NativeMemory.free(namePointer);
+ NativeMemory.free(docPointer);
+ NativeMemory.free(pointer);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyProcsWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyProcsWrapper.java
deleted file mode 100644
index 8e9cbf5bcb..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyProcsWrapper.java
+++ /dev/null
@@ -1,1462 +0,0 @@
-/*
- * Copyright (c) 2018, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.checkThrowableBeforeNative;
-import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY;
-
-import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.PythonBuiltinClassType;
-import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonStructNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeNode;
-import com.oracle.graal.python.builtins.objects.function.PKeyword;
-import com.oracle.graal.python.builtins.objects.ints.PInt;
-import com.oracle.graal.python.builtins.objects.type.TpSlots;
-import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlot;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotManaged;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.CallSlotBinaryFuncNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.CallSlotBinaryOpNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.ReversibleSlot;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet.CallSlotDescrSet;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.CallManagedSlotGetAttrNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.CallSlotHashFunNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.CallSlotNbBoolNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.CallSlotTpIterNextNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotLen.CallSlotLenNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotMpAssSubscript.CallSlotMpAssSubscriptNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotNbPower.CallSlotNbInPlacePowerNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotNbPower.CallSlotNbPowerNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.CallSlotRichCmpNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSetAttr.CallManagedSlotSetAttrNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFun.CallSlotSizeArgFun;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqAssItem.CallSlotSqAssItemNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqContains.CallSlotSqContainsNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotUnaryFunc.CallSlotUnaryNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.CallSlotTpCallNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.CallSlotTpInitNode;
-import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.CallSlotTpNewNode;
-import com.oracle.graal.python.lib.IteratorExhausted;
-import com.oracle.graal.python.lib.RichCmpOp;
-import com.oracle.graal.python.nodes.ErrorMessages;
-import com.oracle.graal.python.nodes.PRaiseNode;
-import com.oracle.graal.python.nodes.argument.keywords.ExpandKeywordStarargsNode;
-import com.oracle.graal.python.nodes.argument.positional.ExecutePositionalStarargsNode;
-import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode;
-import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
-import com.oracle.graal.python.nodes.object.GetClassNode;
-import com.oracle.graal.python.runtime.GilNode;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.graal.python.runtime.exception.PException;
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.GenerateCached;
-import com.oracle.truffle.api.dsl.GenerateInline;
-import com.oracle.truffle.api.dsl.GenerateUncached;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.ArityException;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.interop.UnsupportedTypeException;
-import com.oracle.truffle.api.library.CachedLibrary;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.profiles.InlinedBranchProfile;
-import com.oracle.truffle.api.profiles.InlinedConditionProfile;
-import com.oracle.truffle.nfi.api.SignatureLibrary;
-
-@ExportLibrary(InteropLibrary.class)
-public abstract class PyProcsWrapper extends PythonStructNativeWrapper {
-
- protected final CApiTiming timing;
-
- public PyProcsWrapper(Object delegate) {
- super(delegate);
- this.timing = CApiTiming.create(false, delegate);
- }
-
- @ExportMessage
- protected boolean isExecutable() {
- return true;
- }
-
- @ExportMessage
- @SuppressWarnings({"unused", "static-method"})
- protected Object execute(Object[] arguments) throws UnsupportedTypeException, ArityException, UnsupportedMessageException {
- throw CompilerDirectives.shouldNotReachHere("abstract class");
- }
-
- @ExportMessage
- protected boolean isPointer(
- @Bind Node inliningTarget) {
- if (PythonLanguage.get(inliningTarget).isSingleContext()) {
- return isNative();
- }
- return getClosurePointerMultiContext() != -1;
- }
-
- @ExportMessage
- protected long asPointer(
- @Bind Node inliningTarget) throws UnsupportedMessageException {
- if (PythonLanguage.get(inliningTarget).isSingleContext()) {
- return getNativePointer();
- }
- long pointer = getClosurePointerMultiContext();
- if (pointer == -1) {
- throw UnsupportedMessageException.create();
- }
- return pointer;
- }
-
- @TruffleBoundary
- private long getClosurePointerMultiContext() {
- return PythonContext.get(null).getCApiContext().getClosurePointer(this);
- }
-
- protected abstract String getSignature();
-
- @ExportMessage
- @TruffleBoundary
- protected void toNative(
- @CachedLibrary(limit = "1") SignatureLibrary signatureLibrary) {
- if (!isPointer(null)) {
- CApiContext cApiContext = PythonContext.get(null).getCApiContext();
- long pointer = cApiContext.registerClosure(getSignature(), this, getDelegate(), signatureLibrary);
- if (PythonLanguage.get(null).isSingleContext()) {
- setNativePointer(pointer);
- }
- }
- }
-
- public abstract static class TpSlotWrapper extends PyProcsWrapper {
- public TpSlotWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- public final TpSlotManaged getSlot() {
- return (TpSlotManaged) getDelegate();
- }
-
- public abstract TpSlotWrapper cloneWith(TpSlotManaged slot);
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class GetAttrWrapper extends TpSlotWrapper {
- public GetAttrWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallManagedSlotGetAttrNode callGetAttr,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length < 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(2, -1, arguments.length);
- }
- try {
- return toNativeNode.execute(callGetAttr.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]), toJavaNode.execute(arguments[1])));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "GetAttrWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER):POINTER";
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new GetAttrWrapper(slot);
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class BinaryFuncWrapper extends PyProcsWrapper {
-
- public BinaryFuncWrapper(Object delegate) {
- super(delegate);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallBinaryMethodNode executeNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(2, 2, arguments.length);
- }
- try {
- return toNativeNode.execute(executeNode.executeObject(null, getDelegate(), toJavaNode.execute(arguments[0]), toJavaNode.execute(arguments[1])));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "BinaryFuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class BinarySlotFuncWrapper extends TpSlotWrapper {
-
- public BinarySlotFuncWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallSlotBinaryFuncNode callSlotNode,
- @Cached NativeToPythonNode selfToJavaNode,
- @Cached NativeToPythonNode argTtoJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(2, 2, arguments.length);
- }
- try {
- return toNativeNode.execute(callSlotNode.execute(null, inliningTarget, getSlot(), selfToJavaNode.execute(arguments[0]), argTtoJavaNode.execute(arguments[1])));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "BinaryFuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new BinarySlotFuncWrapper(slot);
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class BinaryOpSlotFuncWrapper extends TpSlotWrapper {
- private final ReversibleSlot binaryOp;
-
- public BinaryOpSlotFuncWrapper(TpSlotManaged delegate, ReversibleSlot binaryOp) {
- super(delegate);
- this.binaryOp = binaryOp;
- }
-
- public static BinaryOpSlotFuncWrapper createAdd(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_ADD);
- }
-
- public static BinaryOpSlotFuncWrapper createSubtract(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_SUBTRACT);
- }
-
- public static BinaryOpSlotFuncWrapper createMultiply(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_MULTIPLY);
- }
-
- public static BinaryOpSlotFuncWrapper createRemainder(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_REMAINDER);
- }
-
- public static BinaryOpSlotFuncWrapper createLShift(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_LSHIFT);
- }
-
- public static BinaryOpSlotFuncWrapper createRShift(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_RSHIFT);
- }
-
- public static BinaryOpSlotFuncWrapper createAnd(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_AND);
- }
-
- public static BinaryOpSlotFuncWrapper createXor(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_XOR);
- }
-
- public static BinaryOpSlotFuncWrapper createOr(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_OR);
- }
-
- public static BinaryOpSlotFuncWrapper createFloorDivide(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_FLOOR_DIVIDE);
- }
-
- public static BinaryOpSlotFuncWrapper createTrueDivide(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_TRUE_DIVIDE);
- }
-
- public static BinaryOpSlotFuncWrapper createDivMod(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_DIVMOD);
- }
-
- public static BinaryOpSlotFuncWrapper createMatrixMultiply(TpSlotManaged delegate) {
- return new BinaryOpSlotFuncWrapper(delegate, ReversibleSlot.NB_MATRIX_MULTIPLY);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallSlotBinaryOpNode callSlotNode,
- @Cached NativeToPythonNode selfToJavaNode,
- @Cached NativeToPythonNode argTtoJavaNode,
- @Cached GetClassNode getSelfClassNode,
- @Cached GetClassNode getOtherClassNode,
- @Cached IsSameTypeNode isSameTypeNode,
- @Cached GetCachedTpSlotsNode getOtherSlots,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(2, 2, arguments.length);
- }
- try {
- Object self = selfToJavaNode.execute(arguments[0]);
- Object other = argTtoJavaNode.execute(arguments[1]);
- Object otherType = getOtherClassNode.execute(inliningTarget, other);
- Object selfType = getSelfClassNode.execute(inliningTarget, self);
- TpSlot otherSlot = binaryOp.getSlotValue(getOtherSlots.execute(inliningTarget, otherType));
- boolean sameTypes = isSameTypeNode.execute(inliningTarget, selfType, otherType);
- return toNativeNode.execute(callSlotNode.execute(null, inliningTarget, getSlot(), self, selfType, other, otherSlot, otherType, sameTypes, binaryOp));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "BinaryFuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new BinaryOpSlotFuncWrapper(slot, binaryOp);
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class UnaryFuncLegacyWrapper extends PyProcsWrapper {
-
- public UnaryFuncLegacyWrapper(Object delegate) {
- super(delegate);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallUnaryMethodNode executeNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- /*
- * Accept a second argumenthere, since these functions are sometimes called using
- * METH_O with a "NULL" value.
- */
- if (arguments.length > 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(1, 2, arguments.length);
- }
- try {
- return toNativeNode.execute(executeNode.executeObject(null, getDelegate(), toJavaNode.execute(arguments[0])));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "UnaryFuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class UnaryFuncWrapper extends TpSlotWrapper {
-
- public UnaryFuncWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new UnaryFuncWrapper(slot);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallSlotUnaryNode callNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- try {
- Object result = callNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]));
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "UnaryFuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class IterNextWrapper extends TpSlotWrapper {
-
- public IterNextWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new IterNextWrapper(slot);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallSlotTpIterNextNode callNextNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- try {
- Object result;
- try {
- result = callNextNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]));
- } catch (IteratorExhausted e) {
- return PythonContext.get(inliningTarget).getNativeNull();
- }
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "UnaryFuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class InquiryWrapper extends TpSlotWrapper {
- public InquiryWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallSlotNbBoolNode callSlotNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length < 1) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(1, -1, arguments.length);
- }
- try {
- return callSlotNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "InquiryWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER):SINT32";
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new InquiryWrapper(slot);
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class SqContainsWrapper extends TpSlotWrapper {
- public SqContainsWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallSlotSqContainsNode callSlotNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- try {
- return callSlotNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]), toJavaNode.execute(arguments[1]));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "SqContainsWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER):SINT32";
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new SqContainsWrapper(slot);
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class ObjobjargWrapper extends TpSlotWrapper {
-
- public ObjobjargWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new ObjobjargWrapper(slot);
- }
-
- @ExportMessage
- int execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallSlotMpAssSubscriptNode callNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached InlinedConditionProfile arityProfile,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arityProfile.profile(inliningTarget, arguments.length != 3)) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, 3, arguments.length);
- }
- try {
- callNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]), toJavaNode.execute(arguments[1]), toJavaNode.execute(arguments[2]));
- return 0;
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "ObjobjargWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):SINT32";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class SetattrWrapper extends TpSlotWrapper {
- public SetattrWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @ExportMessage
- int execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallManagedSlotSetAttrNode callSlotNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached InlinedConditionProfile arityProfile,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arityProfile.profile(inliningTarget, arguments.length < 3)) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, -1, arguments.length);
- }
- try {
- callSlotNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]), toJavaNode.execute(arguments[1]), toJavaNode.execute(arguments[2]));
- return 0;
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "SetattrWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):SINT32";
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new SetattrWrapper(slot);
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class DescrSetFunctionWrapper extends TpSlotWrapper {
- public DescrSetFunctionWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @ExportMessage
- int execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallSlotDescrSet callSetNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length < 3) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, -1, arguments.length);
- }
- try {
- callSetNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]), toJavaNode.execute(arguments[1]), toJavaNode.execute(arguments[2]));
- return 0;
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "SetAttrWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):SINT32";
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new DescrSetFunctionWrapper(slot);
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class InitWrapper extends TpSlotWrapper {
-
- public InitWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new InitWrapper(slot);
- }
-
- @ExportMessage
- int execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached ExecutePositionalStarargsNode posStarargsNode,
- @Cached ExpandKeywordStarargsNode expandKwargsNode,
- @Cached CallSlotTpInitNode callSlot,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Cached GilNode gil) {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- try {
- // convert args
- Object receiver = toJavaNode.execute(arguments[0]);
- Object starArgs = toJavaNode.execute(arguments[1]);
- Object kwArgs = toJavaNode.execute(arguments[2]);
-
- Object[] starArgsArray = posStarargsNode.executeWith(null, starArgs);
- PKeyword[] kwArgsArray = expandKwargsNode.execute(inliningTarget, kwArgs);
- callSlot.execute(null, inliningTarget, getSlot(), receiver, starArgsArray, kwArgsArray);
- return 0;
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "InitWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):SINT32";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class NewWrapper extends TpSlotWrapper {
-
- public NewWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new NewWrapper(slot);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached NativeToPythonNode toJavaNode,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallSlotTpNewNode callNew,
- @Cached ExecutePositionalStarargsNode posStarargsNode,
- @Cached ExpandKeywordStarargsNode expandKwargsNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- try {
- try {
- // convert args
- Object receiver = toJavaNode.execute(arguments[0]);
- Object starArgs = toJavaNode.execute(arguments[1]);
- Object kwArgs = toJavaNode.execute(arguments[2]);
-
- Object[] pArgs;
- if (starArgs != PNone.NO_VALUE) {
- pArgs = posStarargsNode.executeWith(null, starArgs);
- } else {
- pArgs = EMPTY_OBJECT_ARRAY;
- }
- PKeyword[] kwArgsArray = expandKwargsNode.execute(inliningTarget, kwArgs);
-
- Object result = callNew.execute(null, inliningTarget, getSlot(), receiver, pArgs, kwArgsArray);
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "NewWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(inliningTarget).getNativeNull();
- } finally {
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class CallWrapper extends TpSlotWrapper {
-
- public CallWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new CallWrapper(slot);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached ExecutePositionalStarargsNode posStarargsNode,
- @Cached ExpandKeywordStarargsNode expandKwargsNode,
- @Cached CallSlotTpCallNode callNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Cached GilNode gil) {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- try {
- // convert args
- Object receiver = toJavaNode.execute(arguments[0]);
- Object starArgs = toJavaNode.execute(arguments[1]);
- Object kwArgs = toJavaNode.execute(arguments[2]);
-
- Object[] starArgsArray = posStarargsNode.executeWith(null, starArgs);
- PKeyword[] kwArgsArray = expandKwargsNode.execute(inliningTarget, kwArgs);
- Object result = callNode.execute(null, inliningTarget, getSlot(), receiver, starArgsArray, kwArgsArray);
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "CallWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class NbPowerWrapper extends TpSlotWrapper {
-
- public NbPowerWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new NbPowerWrapper(slot);
- }
-
- @ExportMessage
- static Object execute(NbPowerWrapper self, Object[] arguments,
- @Bind Node inliningTarget,
- @Cached NativeToPythonNode toJavaNode,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Cached GetClassNode vGetClassNode,
- @Cached GetClassNode wGetClassNode,
- @Cached IsSameTypeNode isSameTypeNode,
- @Cached GetCachedTpSlotsNode wGetSlots,
- @Cached CallSlotNbPowerNode callSlot,
- @Cached GilNode gil) {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- try {
- // convert args
- Object v = toJavaNode.execute(arguments[0]);
- Object w = toJavaNode.execute(arguments[1]);
- Object z = toJavaNode.execute(arguments[2]);
- Object vType = vGetClassNode.execute(inliningTarget, v);
- Object wType = wGetClassNode.execute(inliningTarget, w);
- TpSlots wSlots = wGetSlots.execute(inliningTarget, wType);
- boolean sameTypes = isSameTypeNode.execute(inliningTarget, vType, wType);
- Object result = callSlot.execute(null, inliningTarget, self.getSlot(), v, vType, w, wSlots.nb_power(), wType, z, sameTypes);
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "NbPowerWrapper", self.getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(self.timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class NbInPlacePowerWrapper extends TpSlotWrapper {
-
- public NbInPlacePowerWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new NbInPlacePowerWrapper(slot);
- }
-
- @ExportMessage
- static Object execute(NbInPlacePowerWrapper self, Object[] arguments,
- @Bind Node inliningTarget,
- @Cached NativeToPythonNode toJavaNode,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Cached CallSlotNbInPlacePowerNode callSlot,
- @Cached GilNode gil) {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- try {
- // convert args
- Object v = toJavaNode.execute(arguments[0]);
- Object w = toJavaNode.execute(arguments[1]);
- Object z = toJavaNode.execute(arguments[2]);
- Object result = callSlot.execute(null, inliningTarget, self.getSlot(), v, w, z);
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "NbInPlacePowerWrapper", self.getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(self.timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class RichcmpFunctionWrapper extends TpSlotWrapper {
-
- public RichcmpFunctionWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new RichcmpFunctionWrapper(slot);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached NativeToPythonNode toJavaNode,
- @Cached CallSlotRichCmpNode callNode,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @CachedLibrary(limit = "1") InteropLibrary opInterop,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 3) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, 3, arguments.length);
- }
- try {
- // convert args
- Object arg0 = toJavaNode.execute(arguments[0]);
- Object arg1 = toJavaNode.execute(arguments[1]);
- RichCmpOp op = RichCmpOp.fromNative(opInterop.asInt(arguments[2]));
- Object result = callNode.execute(null, inliningTarget, getSlot(), arg0, arg1, op);
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "RichcmpFunctionWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,SINT32):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class SsizeargfuncWrapper extends PyProcsWrapper {
-
- public SsizeargfuncWrapper(Object delegate) {
- super(delegate);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallBinaryMethodNode executeNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(2, 2, arguments.length);
- }
- assert arguments[1] instanceof Number;
- try {
- Object result = executeNode.executeObject(null, getDelegate(), toJavaNode.execute(arguments[0]), arguments[1]);
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "SsizeargfuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(toJavaNode).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,SINT64):POINTER";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class SsizeargfuncSlotWrapper extends TpSlotWrapper {
-
- public SsizeargfuncSlotWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new SsizeargfuncSlotWrapper(slot);
- }
-
- @ExportMessage
- Object execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached CallSlotSizeArgFun callSlotNode,
- @Cached SsizeAsIntNode asIntNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(2, 2, arguments.length);
- }
- assert arguments[1] instanceof Number;
- try {
- Object result = callSlotNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]), asIntNode.execute(inliningTarget, arguments[1]));
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "SsizeargfuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(toJavaNode).getNativeNull();
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,SINT64):POINTER";
- }
- }
-
- /**
- * For the time being when indices/lengths in GraalPy are 32bit integers, we must deal with
- * possible situation that someone passes larger number to us. In long term, we should migrate
- * indices/length to use longs.
- */
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- abstract static class SsizeAsIntNode extends Node {
- public abstract int execute(Node inliningTarget, Object value);
-
- @Specialization
- static int doI(int i) {
- return i;
- }
-
- @Specialization
- static int doL(Node inliningTarget, long l,
- @Cached InlinedBranchProfile errorBranch) {
- if (PInt.isIntRange(l)) {
- return (int) l;
- }
- errorBranch.enter(inliningTarget);
- throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.IndexError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, l);
- }
-
- @Fallback
- @InliningCutoff
- static int doOthers(Object value) {
- throw CompilerDirectives.shouldNotReachHere("Unexpected value passed to upcall as Py_ssize_t");
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class SsizeobjargprocWrapper extends TpSlotWrapper {
-
- public SsizeobjargprocWrapper(TpSlotManaged slot) {
- super(slot);
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new SsizeobjargprocWrapper(slot);
- }
-
- @ExportMessage
- int execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallSlotSqAssItemNode executeNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached SsizeAsIntNode asIntNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length != 3) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, 3, arguments.length);
- }
- assert arguments[1] instanceof Number;
- try {
- Object self = toJavaNode.execute(arguments[0]);
- int key = asIntNode.execute(inliningTarget, arguments[1]);
- Object value = toJavaNode.execute(arguments[2]);
- executeNode.execute(null, inliningTarget, getSlot(), self, key, value);
- return 0;
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "SsizeobjargprocWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,SINT64,POINTER):SINT32";
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class LenfuncWrapper extends TpSlotWrapper {
- public LenfuncWrapper(TpSlotManaged managedSlot) {
- super(managedSlot);
- }
-
- @ExportMessage
- long execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallSlotLenNode callSlotNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- if (arguments.length < 1) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(1, -1, arguments.length);
- }
- try {
- return callSlotNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "LenfuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER):SINT64";
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new LenfuncWrapper(slot);
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class HashfuncWrapper extends TpSlotWrapper {
-
- public HashfuncWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @ExportMessage
- long execute(Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallSlotHashFunNode callSlotNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) throws ArityException {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- /*
- * Accept a second argumenthere, since these functions are sometimes called using
- * METH_O with a "NULL" value.
- */
- if (arguments.length > 2) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(1, 2, arguments.length);
- }
- try {
- return callSlotNode.execute(null, inliningTarget, getSlot(), toJavaNode.execute(arguments[0]));
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "HashfuncWrapper", getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return -1;
- } finally {
- CApiTiming.exit(timing);
- gil.release(mustRelease);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER):SINT64";
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new HashfuncWrapper(slot);
- }
- }
-
- @ExportLibrary(InteropLibrary.class)
- public static final class DescrGetFunctionWrapper extends TpSlotWrapper {
- public DescrGetFunctionWrapper(TpSlotManaged delegate) {
- super(delegate);
- }
-
- @ExportMessage(name = "execute")
- static class Execute {
-
- @Specialization(guards = "arguments.length == 3")
- static Object call(DescrGetFunctionWrapper self, Object[] arguments,
- @Bind Node inliningTarget,
- @Cached CallSlotDescrGet callGetNode,
- @Cached NativeToPythonNode toJavaNode,
- @Cached PythonToNativeNewRefNode toNativeNode,
- @Cached TransformPExceptionToNativeNode transformExceptionToNativeNode,
- @Exclusive @Cached GilNode gil) {
- boolean mustRelease = gil.acquire();
- CApiTiming.enter();
- try {
- try {
- if (arguments.length < 3) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, -1, arguments.length);
- }
-
- // convert args
- Object receiver = toJavaNode.execute(arguments[0]);
- Object obj = toJavaNode.execute(arguments[1]);
- Object cls = toJavaNode.execute(arguments[2]);
-
- Object result = callGetNode.execute(null, inliningTarget, self.getSlot(), receiver, obj, cls);
- return toNativeNode.execute(result);
- } catch (Throwable t) {
- throw checkThrowableBeforeNative(t, "DescrGetFunctionWrapper", self.getDelegate());
- }
- } catch (PException e) {
- transformExceptionToNativeNode.execute(inliningTarget, e);
- return PythonContext.get(gil).getNativeNull();
- } finally {
- CApiTiming.exit(self.timing);
- gil.release(mustRelease);
- }
- }
-
- @Specialization(guards = "arguments.length != 3")
- static Object error(@SuppressWarnings("unused") DescrGetFunctionWrapper self, Object[] arguments) throws ArityException {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw ArityException.create(3, 3, arguments.length);
- }
- }
-
- @Override
- protected String getSignature() {
- return "(POINTER,POINTER,POINTER):POINTER";
- }
-
- @Override
- public TpSlotWrapper cloneWith(TpSlotManaged slot) {
- return new DescrGetFunctionWrapper(slot);
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PySequenceArrayWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PySequenceArrayWrapper.java
index 9c1c5f63f5..dad4da5671 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PySequenceArrayWrapper.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PySequenceArrayWrapper.java
@@ -158,7 +158,7 @@ static boolean isMroSequenceStorage(SequenceStorage s) {
}
@TruffleBoundary
- public static Object ensureNativeSequence(PSequence sequence) {
+ public static long ensureNativeSequence(PSequence sequence) {
boolean loggable = LOGGER.isLoggable(Level.FINE);
if (loggable) {
LOGGER.fine(String.format("ensureNativeSequence(%s)", sequence));
@@ -169,7 +169,7 @@ public static Object ensureNativeSequence(PSequence sequence) {
* Hence, if an MroSequenceStorage goes to native, we will create an additional
* NativeSequenceStorage and link to it.
*/
- Object result;
+ long result;
SequenceStorage sequenceStorage = sequence.getSequenceStorage();
if (sequenceStorage instanceof NativeSequenceStorage nativeStorage) {
result = nativeStorage.getPtr();
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonClassNativeWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonClassNativeWrapper.java
deleted file mode 100644
index 4222202b08..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonClassNativeWrapper.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CStringWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
-import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.AllocateNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
-import com.oracle.graal.python.builtins.objects.type.PythonClass;
-import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
-import com.oracle.graal.python.builtins.objects.type.TypeFlags;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetTypeFlagsNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.SetTypeFlagsNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.SetBasicSizeNodeGen;
-import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.SetItemSizeNodeGen;
-import com.oracle.graal.python.nodes.HiddenAttr;
-import com.oracle.graal.python.nodes.PGuards;
-import com.oracle.graal.python.nodes.object.GetClassNode;
-import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.strings.TruffleString;
-
-/**
- * Used to wrap {@link PythonClass} when used in native code. This wrapper is replacing and the
- * replacement mimics the correct shape of the corresponding native type {@code struct _typeobject}.
- */
-public final class PythonClassNativeWrapper extends PythonAbstractObjectNativeWrapper {
- private final CStringWrapper nameWrapper;
- private Object replacement;
-
- private PythonClassNativeWrapper(PythonManagedClass object, TruffleString name, TruffleString.SwitchEncodingNode switchEncoding) {
- super(object);
- this.nameWrapper = new CStringWrapper(switchEncoding.execute(name, TruffleString.Encoding.UTF_8), TruffleString.Encoding.UTF_8);
- }
-
- public CStringWrapper getNameWrapper() {
- return nameWrapper;
- }
-
- public static PythonClassNativeWrapper wrap(PythonManagedClass obj, TruffleString name, TruffleString.SwitchEncodingNode switchEncoding) {
- // important: native wrappers are cached
- PythonClassNativeWrapper nativeWrapper = obj.getClassNativeWrapper();
- if (nativeWrapper == null) {
- nativeWrapper = new PythonClassNativeWrapper(obj, name, switchEncoding);
- obj.setNativeWrapper(nativeWrapper);
- }
- return nativeWrapper;
- }
-
- /**
- * Creates a wrapper that uses existing native memory as native replacement object.
- */
- public static void wrapStaticTypeStructForManagedClass(PythonManagedClass clazz, TruffleString name, Object pointer) {
- /*
- * This *MUST NOT* happen, otherwise we would allocate a fresh native type store and then
- * the native pointer of the wrapper would not be equal to the corresponding native global
- * variable. E.g. 'Py_TYPE(PyBaseObjec_Type) != &PyType_Type'.
- */
- if (clazz.getNativeWrapper() != null) {
- throw CompilerDirectives.shouldNotReachHere();
- }
-
- PythonClassNativeWrapper wrapper = new PythonClassNativeWrapper(clazz, name, TruffleString.SwitchEncodingNode.getUncached());
- clazz.setNativeWrapper(wrapper);
-
- CStructAccess.ReadI64Node readI64 = CStructAccess.ReadI64Node.getUncached();
- CStructAccess.ReadPointerNode readPointer = CStructAccess.ReadPointerNode.getUncached();
- InteropLibrary lib = InteropLibrary.getUncached();
-
- // some values are retained from the native representation
- long basicsize = readI64.read(pointer, CFields.PyTypeObject__tp_basicsize);
- if (basicsize != 0) {
- SetBasicSizeNodeGen.getUncached().execute(null, clazz, basicsize);
- }
- long itemsize = readI64.read(pointer, CFields.PyTypeObject__tp_itemsize);
- if (itemsize != 0) {
- SetItemSizeNodeGen.getUncached().execute(null, clazz, itemsize);
- }
- long vectorcall_offset = readI64.read(pointer, CFields.PyTypeObject__tp_vectorcall_offset);
- if (vectorcall_offset != 0) {
- HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.VECTORCALL_OFFSET, vectorcall_offset);
- }
- Object alloc_fun = readPointer.read(pointer, CFields.PyTypeObject__tp_alloc);
- if (!PGuards.isNullOrZero(alloc_fun, lib)) {
- HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.ALLOC, alloc_fun);
- }
- Object dealloc_fun = readPointer.read(pointer, CFields.PyTypeObject__tp_dealloc);
- if (!PGuards.isNullOrZero(dealloc_fun, lib)) {
- HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.DEALLOC, dealloc_fun);
- }
- Object free_fun = readPointer.read(pointer, CFields.PyTypeObject__tp_free);
- if (!PGuards.isNullOrZero(free_fun, lib)) {
- HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.FREE, free_fun);
- }
- Object traverse_fun = readPointer.read(pointer, CFields.PyTypeObject__tp_traverse);
- if (!PGuards.isNullOrZero(traverse_fun, lib)) {
- HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.TRAVERSE, traverse_fun);
- }
- Object is_gc_fun = readPointer.read(pointer, CFields.PyTypeObject__tp_is_gc);
- if (!PGuards.isNullOrZero(is_gc_fun, lib)) {
- HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.IS_GC, is_gc_fun);
- }
- Object clear_fun = readPointer.read(pointer, CFields.PyTypeObject__tp_clear);
- if (!PGuards.isNullOrZero(clear_fun, lib)) {
- HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.CLEAR, clear_fun);
- }
- Object as_buffer = readPointer.read(pointer, CFields.PyTypeObject__tp_as_buffer);
- if (!PGuards.isNullOrZero(as_buffer, lib)) {
- HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.AS_BUFFER, as_buffer);
- }
-
- /*
- * Initialize type flags: If the native type, we are wrapping, already defines 'tp_flags',
- * we use it because those must stay consistent with slots. For example, native
- * tp_new/tp_alloc/tp_dealloc/tp_free functions must be consistent with
- * 'Py_TPFLAGS_HAVE_GC'.
- */
- long flags = readI64.read(pointer, CFields.PyTypeObject__tp_flags);
- if (flags == 0) {
- flags = GetTypeFlagsNode.executeUncached(clazz) | TypeFlags.READY | TypeFlags.IMMUTABLETYPE;
- }
- SetTypeFlagsNode.executeUncached(clazz, flags);
-
- /*
- * It's important that we first register the pointer before initializing the type (see
- * 'getReplacement' for more explanation).
- */
- wrapper.replacement = pointer;
- long ptr;
- try {
- ptr = lib.asPointer(pointer);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- CApiTransitions.createReference(wrapper, ptr, false);
- }
-
- public static void initNative(PythonManagedClass clazz, Object pointer) {
- PythonClassNativeWrapper classNativeWrapper = clazz.getClassNativeWrapper();
- if (classNativeWrapper == null) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- ToNativeTypeNode.initializeType(classNativeWrapper, pointer, false);
- }
-
- @Override
- public String toString() {
- CompilerAsserts.neverPartOfCompilation();
- return PythonUtils.formatJString("PythonClassNativeWrapper(%s, isNative=%s)", getDelegate(), isNative());
- }
-
- public Object getReplacement() {
- if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, replacement == null)) {
- initializeReplacement();
- }
- return replacement;
- }
-
- @TruffleBoundary
- private void initializeReplacement() {
- /*
- * Note: it's important that we first allocate the empty 'PyTypeStruct' and register it to
- * the wrapper before we do the type's initialization. Otherwise, we will run into an
- * infinite recursion because, e.g., some type uses 'None', so the 'NoneType' will be
- * transformed to native but 'NoneType' may have some field that is initialized with 'None'
- * and so on.
- *
- * If we first set the empty struct and initialize it afterward, everything is fine.
- */
- PythonManagedClass clazz = (PythonManagedClass) getDelegate();
- boolean heaptype = (GetTypeFlagsNode.executeUncached(clazz) & TypeFlags.HEAPTYPE) != 0;
- long size = CStructs.PyTypeObject.size();
- if (heaptype) {
- size = CStructs.PyHeapTypeObject.size();
- if (GetClassNode.executeUncached(clazz) instanceof PythonAbstractNativeObject nativeMetatype) {
- // TODO should call the metatype's tp_alloc
- size = TypeNodes.GetBasicSizeNode.executeUncached(nativeMetatype);
- }
- }
- long ptr = AllocateNode.allocUncachedPointer(size);
- // TODO: need to convert to interop pointer for NFI for now
- replacement = new NativePointer(ptr);
- CApiTransitions.createReference(this, ptr, true);
- ToNativeTypeNode.initializeType(this, ptr, heaptype);
- }
-
- /**
- * Does not initialize the replacement.
- */
- public Object getReplacementIfInitialized() {
- return replacement;
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonNativeWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonNativeWrapper.java
deleted file mode 100644
index 4969d230d4..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonNativeWrapper.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonObjectReference;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.profiles.InlinedConditionProfile;
-
-public abstract class PythonNativeWrapper implements TruffleObject {
- private static final long UNINITIALIZED = -1;
-
- private Object delegate;
- private long nativePointer = UNINITIALIZED;
-
- public PythonObjectReference ref;
-
- private PythonNativeWrapper() {
- }
-
- private PythonNativeWrapper(Object delegate) {
- this.delegate = delegate;
- }
-
- public final Object getDelegate() {
- return delegate;
- }
-
- protected final void setDelegate(Object delegate) {
- assert this.delegate == null || this.delegate == delegate;
- this.delegate = delegate;
- }
-
- public final long getNativePointer() {
- return nativePointer;
- }
-
- public final void setNativePointer(long nativePointer) {
- // we should set the pointer just once
- assert this.nativePointer == UNINITIALIZED || this.nativePointer == nativePointer || nativePointer == UNINITIALIZED;
- this.nativePointer = nativePointer;
- }
-
- public final boolean isNative(Node inliningTarget, InlinedConditionProfile hasNativePointerProfile) {
- return hasNativePointerProfile.profile(inliningTarget, nativePointer != UNINITIALIZED);
- }
-
- public final boolean isNative() {
- return nativePointer != UNINITIALIZED;
- }
-
- /**
- * A wrapper for a reference counted object.
- */
- public abstract static class PythonAbstractObjectNativeWrapper extends PythonNativeWrapper {
- /**
- * Reference count of an object that is only referenced by the Java heap - this is larger
- * than 1 since native code sometimes special cases for low refcounts.
- */
- public static final long MANAGED_REFCNT = 10;
-
- public static final long IMMORTAL_REFCNT = 0xFFFFFFFFL; // from include/object.h
-
- protected PythonAbstractObjectNativeWrapper() {
- }
-
- protected PythonAbstractObjectNativeWrapper(Object delegate) {
- super(delegate);
- }
-
- public final long getRefCount() {
- if (isNative()) {
- return CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(getNativePointer()));
- }
- return MANAGED_REFCNT;
- }
-
- public long incRef() {
- assert isNative();
- long pointer = HandlePointerConverter.pointerToStub(getNativePointer());
- long refCount = CApiTransitions.readNativeRefCount(pointer);
- assert refCount >= PythonAbstractObjectNativeWrapper.MANAGED_REFCNT : "invalid refcnt " + refCount + " during incRef in " + Long.toHexString(getNativePointer());
- if (refCount != IMMORTAL_REFCNT) {
- CApiTransitions.writeNativeRefCount(pointer, refCount + 1);
- return refCount + 1;
- }
- return IMMORTAL_REFCNT;
- }
-
- public long decRef() {
- assert isNative();
- long pointer = HandlePointerConverter.pointerToStub(getNativePointer());
- long refCount = CApiTransitions.readNativeRefCount(pointer);
- if (refCount != IMMORTAL_REFCNT) {
- long updatedRefCount = refCount - 1;
- CApiTransitions.writeNativeRefCount(pointer, updatedRefCount);
- assert updatedRefCount >= PythonAbstractObjectNativeWrapper.MANAGED_REFCNT : "invalid refcnt " + updatedRefCount + " during decRef in " + Long.toHexString(getNativePointer());
- return updatedRefCount;
- }
- return refCount;
- }
- }
-
- /**
- * A wrapper for data objects usually reprsented as C structures without reference counting.
- */
- public abstract static class PythonStructNativeWrapper extends PythonNativeWrapper {
-
- protected PythonStructNativeWrapper() {
- }
-
- protected PythonStructNativeWrapper(Object delegate) {
- super(delegate);
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonObjectNativeWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonObjectNativeWrapper.java
deleted file mode 100644
index 1d5432d5b5..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PythonObjectNativeWrapper.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2018, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-// skip GIL
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.profiles.InlinedConditionProfile;
-
-/**
- * Used to wrap {@link PythonAbstractObject} when used in native code. This wrapper mimics the
- * correct shape of the corresponding native type {@code struct _object}.
- */
-@ExportLibrary(InteropLibrary.class)
-public final class PythonObjectNativeWrapper extends PythonAbstractObjectNativeWrapper {
-
- public PythonObjectNativeWrapper(PythonAbstractObject object) {
- super(object);
- }
-
- public static PythonAbstractObjectNativeWrapper wrap(PythonAbstractObject obj, Node inliningTarget, InlinedConditionProfile noWrapperProfile) {
- // important: native wrappers are cached
- PythonAbstractObjectNativeWrapper nativeWrapper = obj.getNativeWrapper();
- if (noWrapperProfile.profile(inliningTarget, nativeWrapper == null)) {
- nativeWrapper = new PythonObjectNativeWrapper(obj);
- obj.setNativeWrapper(nativeWrapper);
- }
- return nativeWrapper;
- }
-
- @Override
- public String toString() {
- CompilerAsserts.neverPartOfCompilation();
- return PythonUtils.formatJString("PythonObjectNativeWrapper(%s, isNative=%s)", getDelegate(), isNative());
- }
-
- @ExportMessage
- boolean isNull() {
- return getDelegate() == PNone.NO_VALUE;
- }
-
- @ExportMessage
- boolean isPointer() {
- return getDelegate() == PNone.NO_VALUE || isNative();
- }
-
- @ExportMessage
- long asPointer() {
- return getDelegate() == PNone.NO_VALUE ? 0L : getNativePointer();
- }
-
- @ExportMessage
- void toNative(
- @Bind Node inliningTarget,
- @Cached CApiTransitions.FirstToNativeNode firstToNativeNode) {
- if (getDelegate() != PNone.NO_VALUE && !isNative()) {
- /*
- * If the wrapped object is a special singleton (e.g. None, True, False, ...) then it
- * should be immortal.
- */
- boolean immortal = CApiGuards.isSpecialSingleton(getDelegate());
- assert !immortal || (getDelegate() instanceof PythonAbstractObject po && PythonContext.get(inliningTarget).getCApiContext().getSingletonNativeWrapper(po) == this);
- setNativePointer(firstToNativeNode.execute(inliningTarget, this, immortal));
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ToNativeTypeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ToNativeTypeNode.java
deleted file mode 100644
index 8b61a6c9b5..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/ToNativeTypeNode.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (c) 2018, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.capi;
-
-import static com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.lookupNativeI64MemberInMRO;
-import static com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.lookupNativeMemberInMRO;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_refcnt;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_type;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_alloc;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_as_buffer;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_clear;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_dealloc;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_del;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_free;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_is_gc;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_traverse;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_vectorcall_offset;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_weaklistoffset;
-
-import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.PythonBuiltinClassType;
-import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNewRefNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CStringWrapper;
-import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.WritePointerNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
-import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
-import com.oracle.graal.python.builtins.objects.common.HashingStorage;
-import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageAddAllToOther;
-import com.oracle.graal.python.builtins.objects.dict.PDict;
-import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
-import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
-import com.oracle.graal.python.builtins.objects.type.TpSlots;
-import com.oracle.graal.python.builtins.objects.type.TpSlots.GetTpSlotsNode;
-import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotMeta;
-import com.oracle.graal.python.builtins.objects.type.TypeFlags;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBaseClassNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBaseClassesNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBasicSizeNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetDictOffsetNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetItemSizeNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetMroStorageNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetSubclassesNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetTypeFlagsNode;
-import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.GetTypeFlagsNodeGen;
-import com.oracle.graal.python.nodes.HiddenAttr;
-import com.oracle.graal.python.nodes.SpecialAttributeNames;
-import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile;
-import com.oracle.graal.python.nodes.object.GetClassNode;
-import com.oracle.graal.python.nodes.object.GetOrCreateDictNode;
-import com.oracle.graal.python.nodes.util.CannotCastException;
-import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.graal.python.runtime.object.PFactory;
-import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.strings.TruffleString;
-
-public abstract class ToNativeTypeNode {
-
- private static Object allocatePyAsyncMethods(TpSlots slots, Object nullValue) {
- Object mem = CStructAccess.AllocateNode.allocUncached(CStructs.PyAsyncMethods);
- CStructAccess.WritePointerNode writePointerNode = CStructAccess.WritePointerNode.getUncached();
- writeGroupSlots(CFields.PyTypeObject__tp_as_async, slots, writePointerNode, mem, nullValue);
- return mem;
- }
-
- private static void writeGroupSlots(CFields groupField, TpSlots slots, WritePointerNode writePointerNode, Object groupPointer, Object nullValue) {
- for (TpSlotMeta def : TpSlotMeta.VALUES) {
- if (def.getNativeGroupOrField() == groupField) {
- writePointerNode.write(groupPointer, def.getNativeField(), def.getNativeValue(slots, nullValue));
- }
- }
- }
-
- private static Object allocatePyMappingMethods(TpSlots slots, Object nullValue) {
- Object mem = CStructAccess.AllocateNode.allocUncached(CStructs.PyMappingMethods);
- CStructAccess.WritePointerNode writePointerNode = CStructAccess.WritePointerNode.getUncached();
- writeGroupSlots(CFields.PyTypeObject__tp_as_mapping, slots, writePointerNode, mem, nullValue);
- return mem;
- }
-
- private static Object allocatePyNumberMethods(TpSlots slots, Object nullValue) {
- Object mem = CStructAccess.AllocateNode.allocUncached(CStructs.PyNumberMethods);
- CStructAccess.WritePointerNode writePointerNode = CStructAccess.WritePointerNode.getUncached();
- writeGroupSlots(CFields.PyTypeObject__tp_as_number, slots, writePointerNode, mem, nullValue);
- return mem;
- }
-
- private static Object allocatePySequenceMethods(TpSlots slots, Object nullValue) {
- Object mem = CStructAccess.AllocateNode.allocUncached(CStructs.PyNumberMethods);
- CStructAccess.WritePointerNode writePointerNode = CStructAccess.WritePointerNode.getUncached();
- writeGroupSlots(CFields.PyTypeObject__tp_as_sequence, slots, writePointerNode, mem, nullValue);
- return mem;
- }
-
- private static Object lookup(PythonManagedClass clazz, CFields member, HiddenAttr hiddenName) {
- Object result = lookupNativeMemberInMRO(clazz, member, hiddenName);
- if (result == PNone.NO_VALUE) {
- return PythonContext.get(null).getNativeNull();
- }
- return result;
- }
-
- private static long lookupSize(PythonManagedClass clazz, CFields member, HiddenAttr hiddenName) {
- return lookupNativeI64MemberInMRO(clazz, member, hiddenName);
- }
-
- static void initializeType(PythonClassNativeWrapper obj, Object mem, boolean heaptype) {
- CompilerAsserts.neverPartOfCompilation();
-
- PythonManagedClass clazz = (PythonManagedClass) obj.getDelegate();
- TpSlots slots = GetTpSlotsNode.executeUncached(clazz);
- boolean isType = IsBuiltinClassExactProfile.profileClassSlowPath(clazz, PythonBuiltinClassType.PythonClass);
-
- PythonToNativeNode toNative = PythonToNativeNodeGen.getUncached();
- PythonToNativeNewRefNode toNativeNewRef = PythonToNativeNewRefNodeGen.getUncached();
- CStructAccess.WritePointerNode writePtrNode = CStructAccessFactory.WritePointerNodeGen.getUncached();
- CStructAccess.WriteLongNode writeI64Node = CStructAccessFactory.WriteLongNodeGen.getUncached();
- CStructAccess.WriteIntNode writeI32Node = CStructAccessFactory.WriteIntNodeGen.getUncached();
- GetTypeFlagsNode getTypeFlagsNode = GetTypeFlagsNodeGen.getUncached();
-
- PythonContext ctx = PythonContext.get(null);
- PythonLanguage language = ctx.getLanguage();
- Object nullValue = ctx.getNativeNull();
-
- // make this object immortal
- writeI64Node.write(mem, PyObject__ob_refcnt, PythonAbstractObjectNativeWrapper.IMMORTAL_REFCNT);
- if (isType) {
- // self-reference
- writePtrNode.write(mem, PyObject__ob_type, mem);
- } else {
- writePtrNode.write(mem, PyObject__ob_type, toNative.execute(GetClassNode.executeUncached(clazz)));
- }
-
- long flags = getTypeFlagsNode.execute(clazz);
- /*
- * Our datetime classes are declared as static types in C, but are implemented as
- * pure-python heaptypes. Make them into static types on the C-side.
- */
- if (!heaptype) {
- flags &= ~TypeFlags.HEAPTYPE;
- }
-
- Object base = GetBaseClassNode.executeUncached(clazz);
- if (base == null) {
- base = ctx.getNativeNull();
- } else if (base instanceof PythonBuiltinClassType builtinClass) {
- base = ctx.lookupType(builtinClass);
- }
-
- writeI64Node.write(mem, CFields.PyVarObject__ob_size, 0L);
-
- writePtrNode.write(mem, CFields.PyTypeObject__tp_name, clazz.getClassNativeWrapper().getNameWrapper());
- writeI64Node.write(mem, CFields.PyTypeObject__tp_basicsize, GetBasicSizeNode.executeUncached(clazz));
- writeI64Node.write(mem, CFields.PyTypeObject__tp_itemsize, GetItemSizeNode.executeUncached(clazz));
- // writeI64Node.write(mem, CFields.PyTypeObject__tp_weaklistoffset,
- // GetWeakListOffsetNode.executeUncached(clazz));
- /*
- * TODO msimacek: this should use GetWeakListOffsetNode as in the commented out code above.
- * Unfortunately, it causes memory corruption in several libraries
- */
- long weaklistoffset;
- if (clazz instanceof PythonBuiltinClass builtin) {
- weaklistoffset = builtin.getType().getWeaklistoffset();
- } else {
- weaklistoffset = lookupNativeI64MemberInMRO(clazz, PyTypeObject__tp_weaklistoffset, SpecialAttributeNames.T___WEAKLISTOFFSET__);
- }
- Object asAsync = slots.has_as_async() ? allocatePyAsyncMethods(slots, nullValue) : nullValue;
- Object asNumber = slots.has_as_number() ? allocatePyNumberMethods(slots, nullValue) : nullValue;
- Object asSequence = slots.has_as_sequence() ? allocatePySequenceMethods(slots, nullValue) : nullValue;
- Object asMapping = slots.has_as_mapping() ? allocatePyMappingMethods(slots, nullValue) : nullValue;
- Object asBuffer = lookup(clazz, PyTypeObject__tp_as_buffer, HiddenAttr.AS_BUFFER);
- writeI64Node.write(mem, CFields.PyTypeObject__tp_weaklistoffset, weaklistoffset);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_dealloc, lookup(clazz, PyTypeObject__tp_dealloc, HiddenAttr.DEALLOC));
- writeI64Node.write(mem, CFields.PyTypeObject__tp_vectorcall_offset, lookupSize(clazz, PyTypeObject__tp_vectorcall_offset, HiddenAttr.VECTORCALL_OFFSET));
- writePtrNode.write(mem, CFields.PyTypeObject__tp_getattr, nullValue);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_as_async, asAsync);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_as_number, asNumber);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_as_sequence, asSequence);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_as_mapping, asMapping);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_as_buffer, asBuffer);
- writeI64Node.write(mem, CFields.PyTypeObject__tp_flags, flags);
-
- // return a C string wrapper that really allocates 'char*' on TO_NATIVE
- Object docObj = clazz.getAttribute(SpecialAttributeNames.T___DOC__);
- try {
- docObj = new CStringWrapper(CastToTruffleStringNode.executeUncached(docObj).switchEncodingUncached(TruffleString.Encoding.UTF_8), TruffleString.Encoding.UTF_8);
- } catch (CannotCastException e) {
- // if not directly a string, give up (we don't call descriptors here)
- docObj = ctx.getNativeNull();
- }
- writePtrNode.write(mem, CFields.PyTypeObject__tp_doc, docObj);
-
- Object tpTraverse = nullValue;
- Object tpIsGc = nullValue;
- if ((flags & TypeFlags.HAVE_GC) != 0) {
- tpTraverse = lookup(clazz, PyTypeObject__tp_traverse, HiddenAttr.TRAVERSE);
- tpIsGc = lookup(clazz, PyTypeObject__tp_is_gc, HiddenAttr.IS_GC);
- }
- writePtrNode.write(mem, CFields.PyTypeObject__tp_traverse, tpTraverse);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_is_gc, tpIsGc);
-
- writePtrNode.write(mem, CFields.PyTypeObject__tp_methods, nullValue);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_members, nullValue);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_getset, nullValue);
- if (!isType) {
- // "object" base needs to be initialized explicitly in capi.c
- writePtrNode.write(mem, CFields.PyTypeObject__tp_base, toNative.execute(base));
- }
-
- // TODO(fa): we could cache the dict instance on the class' native wrapper
- PDict dict = GetOrCreateDictNode.executeUncached(clazz);
- HashingStorage dictStorage = dict.getDictStorage();
- if (!(dictStorage instanceof DynamicObjectStorage)) {
- HashingStorage storage = new DynamicObjectStorage(clazz);
- // copy all mappings to the new storage
- dict.setDictStorage(HashingStorageAddAllToOther.executeUncached(dictStorage, storage));
- }
- writePtrNode.write(mem, CFields.PyTypeObject__tp_dict, toNative.execute(dict));
-
- for (TpSlotMeta def : TpSlotMeta.VALUES) {
- if (!def.hasGroup() && def.hasNativeWrapperFactory()) {
- writePtrNode.write(mem, def.getNativeGroupOrField(), def.getNativeValue(slots, nullValue));
- }
- }
-
- // TODO properly implement 'tp_dictoffset' for builtin classes
- writeI64Node.write(mem, CFields.PyTypeObject__tp_dictoffset, GetDictOffsetNode.executeUncached(clazz));
- writePtrNode.write(mem, CFields.PyTypeObject__tp_alloc, lookup(clazz, PyTypeObject__tp_alloc, HiddenAttr.ALLOC));
- writePtrNode.write(mem, CFields.PyTypeObject__tp_free, lookup(clazz, PyTypeObject__tp_free, HiddenAttr.FREE));
- writePtrNode.write(mem, CFields.PyTypeObject__tp_clear, lookup(clazz, PyTypeObject__tp_clear, HiddenAttr.CLEAR));
- if (clazz.basesTuple == null) {
- clazz.basesTuple = PFactory.createTuple(language, GetBaseClassesNode.executeUncached(clazz));
- }
- writePtrNode.write(mem, CFields.PyTypeObject__tp_bases, toNative.execute(clazz.basesTuple));
- if (clazz.mroStore == null) {
- clazz.mroStore = PFactory.createTuple(language, GetMroStorageNode.executeUncached(clazz));
- }
- writePtrNode.write(mem, CFields.PyTypeObject__tp_mro, toNative.execute(clazz.mroStore));
- writePtrNode.write(mem, CFields.PyTypeObject__tp_cache, nullValue);
- PDict subclasses = GetSubclassesNode.executeUncached(clazz);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_subclasses, toNativeNewRef.execute(subclasses));
- writePtrNode.write(mem, CFields.PyTypeObject__tp_weaklist, nullValue);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_del, lookup(clazz, PyTypeObject__tp_del, HiddenAttr.DEL));
- writeI32Node.write(mem, CFields.PyTypeObject__tp_version_tag, 0);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_finalize, nullValue);
- writePtrNode.write(mem, CFields.PyTypeObject__tp_vectorcall, nullValue);
-
- if (heaptype) {
- assert (flags & TypeFlags.HEAPTYPE) != 0;
- writePtrNode.write(mem, CFields.PyHeapTypeObject__as_async, asAsync);
- writePtrNode.write(mem, CFields.PyHeapTypeObject__as_number, asNumber);
- writePtrNode.write(mem, CFields.PyHeapTypeObject__as_mapping, asMapping);
- writePtrNode.write(mem, CFields.PyHeapTypeObject__as_sequence, asSequence);
- writePtrNode.write(mem, CFields.PyHeapTypeObject__as_buffer, asBuffer);
- writePtrNode.write(mem, CFields.PyHeapTypeObject__ht_name, toNativeNewRef.execute(clazz.getName()));
- writePtrNode.write(mem, CFields.PyHeapTypeObject__ht_qualname, toNativeNewRef.execute(clazz.getQualName()));
- writePtrNode.write(mem, CFields.PyHeapTypeObject__ht_module, nullValue);
- Object dunderSlots = clazz.getAttribute(SpecialAttributeNames.T___SLOTS__);
- writePtrNode.write(mem, CFields.PyHeapTypeObject__ht_slots, dunderSlots != PNone.NO_VALUE ? toNativeNewRef.execute(dunderSlots) : nullValue);
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TpSlotWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TpSlotWrapper.java
new file mode 100644
index 0000000000..c05ce0f87b
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TpSlotWrapper.java
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) 2018, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.builtins.objects.cext.capi;
+
+import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.checkThrowableBeforeNative;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType.RAW_POINTER;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType.SINT32;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType.SINT64;
+import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionToNativeNode;
+import com.oracle.graal.python.builtins.objects.function.PKeyword;
+import com.oracle.graal.python.builtins.objects.ints.PInt;
+import com.oracle.graal.python.builtins.objects.type.TpSlots;
+import com.oracle.graal.python.builtins.objects.type.TpSlots.GetTpSlotsNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlot;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotManaged;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.CallSlotBinaryFuncNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.CallSlotBinaryOpNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.ReversibleSlot;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet.CallSlotDescrSet;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.CallManagedSlotGetAttrNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun.CallSlotHashFunNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.CallSlotNbBoolNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.CallSlotTpIterNextNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotLen.CallSlotLenNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotMpAssSubscript.CallSlotMpAssSubscriptNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotNbPower.CallSlotNbInPlacePowerNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotNbPower.CallSlotNbPowerNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.CallSlotRichCmpNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSetAttr.CallManagedSlotSetAttrNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFun.CallSlotSizeArgFun;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqAssItem.CallSlotSqAssItemNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqContains.CallSlotSqContainsNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotUnaryFunc.CallSlotUnaryNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.CallSlotTpCallNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.CallSlotTpInitNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.CallSlotTpNewNode;
+import com.oracle.graal.python.lib.IteratorExhausted;
+import com.oracle.graal.python.lib.RichCmpOp;
+import com.oracle.graal.python.runtime.nativeaccess.NativeSignature;
+import com.oracle.graal.python.nodes.ErrorMessages;
+import com.oracle.graal.python.nodes.PRaiseNode;
+import com.oracle.graal.python.nodes.argument.keywords.ExpandKeywordStarargsNode;
+import com.oracle.graal.python.nodes.argument.positional.ExecutePositionalStarargsNode;
+import com.oracle.graal.python.nodes.object.GetClassNode;
+import com.oracle.graal.python.runtime.GilNode;
+import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+
+public abstract class TpSlotWrapper {
+
+ private static final NativeSignature SIGNATURE_P_P = NativeSignature.create(RAW_POINTER, RAW_POINTER);
+ private static final NativeSignature SIGNATURE_P_PP = NativeSignature.create(RAW_POINTER, RAW_POINTER, RAW_POINTER);
+ private static final NativeSignature SIGNATURE_P_PPP = NativeSignature.create(RAW_POINTER, RAW_POINTER, RAW_POINTER, RAW_POINTER);
+ private static final NativeSignature SIGNATURE_P_PPI = NativeSignature.create(RAW_POINTER, RAW_POINTER, RAW_POINTER, SINT32);
+ private static final NativeSignature SIGNATURE_P_PL = NativeSignature.create(RAW_POINTER, RAW_POINTER, SINT64);
+ private static final NativeSignature SIGNATURE_I_P = NativeSignature.create(SINT32, RAW_POINTER);
+ private static final NativeSignature SIGNATURE_I_PP = NativeSignature.create(SINT32, RAW_POINTER, RAW_POINTER);
+ private static final NativeSignature SIGNATURE_I_PPP = NativeSignature.create(SINT32, RAW_POINTER, RAW_POINTER, RAW_POINTER);
+ private static final NativeSignature SIGNATURE_I_PLP = NativeSignature.create(SINT32, RAW_POINTER, SINT64, RAW_POINTER);
+ private static final NativeSignature SIGNATURE_L_P = NativeSignature.create(SINT64, RAW_POINTER);
+
+ private static final MethodHandle HANDLE_GET_ATTR;
+ private static final MethodHandle HANDLE_BINARY_SLOT_FUNC;
+ private static final MethodHandle HANDLE_BINARY_OP_SLOT_FUNC;
+ private static final MethodHandle HANDLE_UNARY_FUNC;
+ private static final MethodHandle HANDLE_ITER_NEXT;
+ private static final MethodHandle HANDLE_INQUIRY;
+ private static final MethodHandle HANDLE_SQ_CONTAINS;
+ private static final MethodHandle HANDLE_OBJ_OBJ_ARG;
+ private static final MethodHandle HANDLE_SET_ATTR;
+ private static final MethodHandle HANDLE_DESCR_SET_FUNCTION;
+ private static final MethodHandle HANDLE_INIT;
+ private static final MethodHandle HANDLE_NEW;
+ private static final MethodHandle HANDLE_CALL;
+ private static final MethodHandle HANDLE_NB_POWER;
+ private static final MethodHandle HANDLE_NB_IN_PLACE_POWER;
+ private static final MethodHandle HANDLE_RICHCMP_FUNCTION;
+ private static final MethodHandle HANDLE_SSIZEARGFUNC_SLOT;
+ private static final MethodHandle HANDLE_SSIZEOBJARGPROC;
+ private static final MethodHandle HANDLE_LENFUNC;
+ private static final MethodHandle HANDLE_HASHFUNC;
+ private static final MethodHandle HANDLE_DESCR_GET_FUNCTION;
+
+ static {
+ try {
+ HANDLE_GET_ATTR = MethodHandles.lookup().findStatic(GetAttrWrapper.class, "executeGetAttr", MethodType.methodType(long.class, GetAttrWrapper.class, long.class, long.class));
+ HANDLE_BINARY_SLOT_FUNC = MethodHandles.lookup().findStatic(BinarySlotFuncWrapper.class, "executeBinarySlot",
+ MethodType.methodType(long.class, BinarySlotFuncWrapper.class, long.class, long.class));
+ HANDLE_BINARY_OP_SLOT_FUNC = MethodHandles.lookup().findStatic(BinaryOpSlotFuncWrapper.class, "executeBinaryOpSlot",
+ MethodType.methodType(long.class, BinaryOpSlotFuncWrapper.class, long.class, long.class));
+ HANDLE_UNARY_FUNC = MethodHandles.lookup().findStatic(UnaryFuncWrapper.class, "executeUnary", MethodType.methodType(long.class, UnaryFuncWrapper.class, long.class));
+ HANDLE_ITER_NEXT = MethodHandles.lookup().findStatic(IterNextWrapper.class, "executeIterNext", MethodType.methodType(long.class, IterNextWrapper.class, long.class));
+ HANDLE_INQUIRY = MethodHandles.lookup().findStatic(InquiryWrapper.class, "executeInquiry", MethodType.methodType(int.class, InquiryWrapper.class, long.class));
+ HANDLE_SQ_CONTAINS = MethodHandles.lookup().findStatic(SqContainsWrapper.class, "executeSqContains", MethodType.methodType(int.class, SqContainsWrapper.class, long.class, long.class));
+ HANDLE_OBJ_OBJ_ARG = MethodHandles.lookup().findStatic(ObjobjargWrapper.class, "executeObjobjarg",
+ MethodType.methodType(int.class, ObjobjargWrapper.class, long.class, long.class, long.class));
+ HANDLE_SET_ATTR = MethodHandles.lookup().findStatic(SetAttrWrapper.class, "executeSetAttr",
+ MethodType.methodType(int.class, SetAttrWrapper.class, long.class, long.class, long.class));
+ HANDLE_DESCR_SET_FUNCTION = MethodHandles.lookup().findStatic(DescrSetFunctionWrapper.class, "executeDescrSetFunction",
+ MethodType.methodType(int.class, DescrSetFunctionWrapper.class, long.class, long.class, long.class));
+ HANDLE_INIT = MethodHandles.lookup().findStatic(InitWrapper.class, "executeInit", MethodType.methodType(int.class, InitWrapper.class, long.class, long.class, long.class));
+ HANDLE_NEW = MethodHandles.lookup().findStatic(NewWrapper.class, "executeNew", MethodType.methodType(long.class, NewWrapper.class, long.class, long.class, long.class));
+ HANDLE_CALL = MethodHandles.lookup().findStatic(CallWrapper.class, "executeCall", MethodType.methodType(long.class, CallWrapper.class, long.class, long.class, long.class));
+ HANDLE_NB_POWER = MethodHandles.lookup().findStatic(NbPowerWrapper.class, "executeNbPower", MethodType.methodType(long.class, NbPowerWrapper.class, long.class, long.class, long.class));
+ HANDLE_NB_IN_PLACE_POWER = MethodHandles.lookup().findStatic(NbInPlacePowerWrapper.class, "executeNbInPlacePower",
+ MethodType.methodType(long.class, NbInPlacePowerWrapper.class, long.class, long.class, long.class));
+ HANDLE_RICHCMP_FUNCTION = MethodHandles.lookup().findStatic(RichcmpFunctionWrapper.class, "executeRichcmpFunction",
+ MethodType.methodType(long.class, RichcmpFunctionWrapper.class, long.class, long.class, int.class));
+ HANDLE_SSIZEARGFUNC_SLOT = MethodHandles.lookup().findStatic(SsizeargfuncSlotWrapper.class, "executeSsizeargfuncSlot",
+ MethodType.methodType(long.class, SsizeargfuncSlotWrapper.class, long.class, long.class));
+ HANDLE_SSIZEOBJARGPROC = MethodHandles.lookup().findStatic(SsizeobjargprocWrapper.class, "executeSsizeobjargproc",
+ MethodType.methodType(int.class, SsizeobjargprocWrapper.class, long.class, long.class, long.class));
+ HANDLE_LENFUNC = MethodHandles.lookup().findStatic(LenfuncWrapper.class, "executeLenfunc", MethodType.methodType(long.class, LenfuncWrapper.class, long.class));
+ HANDLE_HASHFUNC = MethodHandles.lookup().findStatic(HashfuncWrapper.class, "executeHashfunc", MethodType.methodType(long.class, HashfuncWrapper.class, long.class));
+ HANDLE_DESCR_GET_FUNCTION = MethodHandles.lookup().findStatic(DescrGetFunctionWrapper.class, "executeDescrGetFunction",
+ MethodType.methodType(long.class, DescrGetFunctionWrapper.class, long.class, long.class, long.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected final CApiTiming timing;
+ private final TpSlotManaged slot;
+ private final long nativePointer;
+
+ @SuppressWarnings("this-escape")
+ TpSlotWrapper(TpSlotManaged slot, NativeSignature upcallSignature, MethodHandle methodHandle) {
+ this.timing = CApiTiming.create(false, slot);
+ this.slot = slot;
+
+ CApiContext cApiContext = PythonContext.get(null).getCApiContext();
+ long pointer = cApiContext.registerClosure(getClass().getSimpleName(), upcallSignature, methodHandle.bindTo(this), this, slot);
+ if (PythonLanguage.get(null).isSingleContext()) {
+ nativePointer = pointer;
+ } else {
+ nativePointer = NULLPTR;
+ }
+ }
+
+ public final TpSlotManaged getSlot() {
+ return slot;
+ }
+
+ @TruffleBoundary
+ public final long getPointer() {
+ if (nativePointer != NULLPTR) {
+ assert PythonLanguage.get(null).isSingleContext();
+ return nativePointer;
+ }
+ return PythonContext.get(null).getCApiContext().getClosurePointer(this);
+ }
+
+ public abstract TpSlotWrapper cloneWith(TpSlotManaged slot);
+
+ public static final class GetAttrWrapper extends TpSlotWrapper {
+ public GetAttrWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PP, HANDLE_GET_ATTR);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeGetAttr(GetAttrWrapper self, long arg0, long arg1) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object result = CallManagedSlotGetAttrNode.executeUncached(self.getSlot(), jArg0, jArg1);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "GetAttrWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new GetAttrWrapper(slot);
+ }
+ }
+
+ public static final class BinarySlotFuncWrapper extends TpSlotWrapper {
+
+ public BinarySlotFuncWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PP, HANDLE_BINARY_SLOT_FUNC);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeBinarySlot(BinarySlotFuncWrapper self, long arg0, long arg1) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object result = CallSlotBinaryFuncNode.executeUncached(self.getSlot(), jArg0, jArg1);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "BinarySlotFuncWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new BinarySlotFuncWrapper(slot);
+ }
+ }
+
+ public static final class BinaryOpSlotFuncWrapper extends TpSlotWrapper {
+ private final ReversibleSlot binaryOp;
+
+ public BinaryOpSlotFuncWrapper(TpSlotManaged slot, ReversibleSlot binaryOp) {
+ super(slot, SIGNATURE_P_PP, HANDLE_BINARY_OP_SLOT_FUNC);
+ this.binaryOp = binaryOp;
+ }
+
+ public static BinaryOpSlotFuncWrapper createAdd(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_ADD);
+ }
+
+ public static BinaryOpSlotFuncWrapper createSubtract(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_SUBTRACT);
+ }
+
+ public static BinaryOpSlotFuncWrapper createMultiply(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_MULTIPLY);
+ }
+
+ public static BinaryOpSlotFuncWrapper createRemainder(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_REMAINDER);
+ }
+
+ public static BinaryOpSlotFuncWrapper createLShift(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_LSHIFT);
+ }
+
+ public static BinaryOpSlotFuncWrapper createRShift(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_RSHIFT);
+ }
+
+ public static BinaryOpSlotFuncWrapper createAnd(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_AND);
+ }
+
+ public static BinaryOpSlotFuncWrapper createXor(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_XOR);
+ }
+
+ public static BinaryOpSlotFuncWrapper createOr(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_OR);
+ }
+
+ public static BinaryOpSlotFuncWrapper createFloorDivide(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_FLOOR_DIVIDE);
+ }
+
+ public static BinaryOpSlotFuncWrapper createTrueDivide(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_TRUE_DIVIDE);
+ }
+
+ public static BinaryOpSlotFuncWrapper createDivMod(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_DIVMOD);
+ }
+
+ public static BinaryOpSlotFuncWrapper createMatrixMultiply(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, ReversibleSlot.NB_MATRIX_MULTIPLY);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeBinaryOpSlot(BinaryOpSlotFuncWrapper self, long arg0, long arg1) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object receiver = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object other = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object otherType = GetClassNode.executeUncached(other);
+ Object receiverType = GetClassNode.executeUncached(receiver);
+ TpSlot otherSlot = self.binaryOp.getSlotValue(GetTpSlotsNode.executeUncached(otherType));
+ boolean sameTypes = IsSameTypeNode.executeUncached(receiverType, otherType);
+ Object result = CallSlotBinaryOpNode.executeUncached(self.getSlot(), receiver, receiverType, other, otherSlot, otherType, sameTypes, self.binaryOp);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "BinaryOpSlotFuncWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new BinaryOpSlotFuncWrapper(slot, binaryOp);
+ }
+ }
+
+ public static final class UnaryFuncWrapper extends TpSlotWrapper {
+
+ public UnaryFuncWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_P, HANDLE_UNARY_FUNC);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeUnary(UnaryFuncWrapper self, long arg0) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object result = CallSlotUnaryNode.executeUncached(self.getSlot(), jArg0);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "UnaryFuncWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new UnaryFuncWrapper(slot);
+ }
+ }
+
+ public static final class IterNextWrapper extends TpSlotWrapper {
+
+ public IterNextWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_P, HANDLE_ITER_NEXT);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeIterNext(IterNextWrapper self, long arg0) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object result;
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ result = CallSlotTpIterNextNode.executeUncached(self.getSlot(), jArg0);
+ } catch (IteratorExhausted e) {
+ return NULLPTR;
+ }
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "IterNextWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new IterNextWrapper(slot);
+ }
+ }
+
+ public static final class InquiryWrapper extends TpSlotWrapper {
+ public InquiryWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_I_P, HANDLE_INQUIRY);
+ }
+
+ @SuppressWarnings("try")
+ private static int executeInquiry(InquiryWrapper self, long arg0) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ return CallSlotNbBoolNode.executeUncached(self.getSlot(), jArg0) ? 1 : 0;
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "InquiryWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new InquiryWrapper(slot);
+ }
+ }
+
+ public static final class SqContainsWrapper extends TpSlotWrapper {
+ public SqContainsWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_I_PP, HANDLE_SQ_CONTAINS);
+ }
+
+ @SuppressWarnings("try")
+ private static int executeSqContains(SqContainsWrapper self, long arg0, long arg1) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false);
+ return CallSlotSqContainsNode.executeUncached(self.getSlot(), jArg0, jArg1) ? 1 : 0;
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "SqContainsWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new SqContainsWrapper(slot);
+ }
+ }
+
+ public static final class ObjobjargWrapper extends TpSlotWrapper {
+
+ public ObjobjargWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_I_PPP, HANDLE_OBJ_OBJ_ARG);
+ }
+
+ @SuppressWarnings("try")
+ private static int executeObjobjarg(ObjobjargWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object jArg2 = NativeToPythonInternalNode.executeUncached(arg2, false);
+ CallSlotMpAssSubscriptNode.executeUncached(self.getSlot(), jArg0, jArg1, jArg2);
+ return 0;
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "ObjobjargWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new ObjobjargWrapper(slot);
+ }
+ }
+
+ public static final class SetAttrWrapper extends TpSlotWrapper {
+ public SetAttrWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_I_PPP, HANDLE_SET_ATTR);
+ }
+
+ @SuppressWarnings("try")
+ private static int executeSetAttr(SetAttrWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object jArg2 = NativeToPythonInternalNode.executeUncached(arg2, false);
+ CallManagedSlotSetAttrNode.executeUncached(self.getSlot(), jArg0, jArg1, jArg2);
+ return 0;
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "SetAttrWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new SetAttrWrapper(slot);
+ }
+ }
+
+ public static final class DescrSetFunctionWrapper extends TpSlotWrapper {
+ public DescrSetFunctionWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_I_PPP, HANDLE_DESCR_SET_FUNCTION);
+ }
+
+ @SuppressWarnings("try")
+ private static int executeDescrSetFunction(DescrSetFunctionWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object jArg2 = NativeToPythonInternalNode.executeUncached(arg2, false);
+ CallSlotDescrSet.executeUncached(self.getSlot(), jArg0, jArg1, jArg2);
+ return 0;
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "DescrSetFunctionWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new DescrSetFunctionWrapper(slot);
+ }
+ }
+
+ public static final class InitWrapper extends TpSlotWrapper {
+
+ public InitWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_I_PPP, HANDLE_INIT);
+ }
+
+ @SuppressWarnings("try")
+ private static int executeInit(InitWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ // convert args
+ Object receiver = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object starArgs = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object kwArgs = NativeToPythonInternalNode.executeUncached(arg2, false);
+
+ Object[] starArgsArray = ExecutePositionalStarargsNode.executeUncached(starArgs);
+ PKeyword[] kwArgsArray = ExpandKeywordStarargsNode.executeUncached(kwArgs);
+ CallSlotTpInitNode.executeUncached(self.getSlot(), receiver, starArgsArray, kwArgsArray);
+ return 0;
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "InitWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new InitWrapper(slot);
+ }
+ }
+
+ public static final class NewWrapper extends TpSlotWrapper {
+
+ public NewWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PPP, HANDLE_NEW);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeNew(NewWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ try {
+ // convert args
+ Object receiver = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object starArgs = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object kwArgs = NativeToPythonInternalNode.executeUncached(arg2, false);
+
+ Object[] pArgs;
+ if (starArgs != PNone.NO_VALUE) {
+ pArgs = ExecutePositionalStarargsNode.executeUncached(starArgs);
+ } else {
+ pArgs = EMPTY_OBJECT_ARRAY;
+ }
+ PKeyword[] kwArgsArray = ExpandKeywordStarargsNode.executeUncached(kwArgs);
+
+ Object result = CallSlotTpNewNode.executeUncached(self.getSlot(), receiver, pArgs, kwArgsArray);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "NewWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new NewWrapper(slot);
+ }
+ }
+
+ public static final class CallWrapper extends TpSlotWrapper {
+
+ public CallWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PPP, HANDLE_CALL);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeCall(CallWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ // convert args
+ Object receiver = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object starArgs = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object kwArgs = NativeToPythonInternalNode.executeUncached(arg2, false);
+
+ Object[] starArgsArray = ExecutePositionalStarargsNode.executeUncached(starArgs);
+ PKeyword[] kwArgsArray = ExpandKeywordStarargsNode.executeUncached(kwArgs);
+ Object result = CallSlotTpCallNode.executeUncached(self.getSlot(), receiver, starArgsArray, kwArgsArray);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "CallWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new CallWrapper(slot);
+ }
+ }
+
+ public static final class NbPowerWrapper extends TpSlotWrapper {
+
+ public NbPowerWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PPP, HANDLE_NB_POWER);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeNbPower(NbPowerWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ // convert args
+ Object v = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object w = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object z = NativeToPythonInternalNode.executeUncached(arg2, false);
+ Object vType = GetClassNode.executeUncached(v);
+ Object wType = GetClassNode.executeUncached(w);
+ TpSlots wSlots = GetTpSlotsNode.executeUncached(wType);
+ boolean sameTypes = IsSameTypeNode.executeUncached(vType, wType);
+ Object result = CallSlotNbPowerNode.executeUncached(self.getSlot(), v, vType, w, wSlots.nb_power(), wType, z, sameTypes);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "NbPowerWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new NbPowerWrapper(slot);
+ }
+ }
+
+ public static final class NbInPlacePowerWrapper extends TpSlotWrapper {
+
+ public NbInPlacePowerWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PPP, HANDLE_NB_IN_PLACE_POWER);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeNbInPlacePower(NbInPlacePowerWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ // convert args
+ Object v = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object w = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object z = NativeToPythonInternalNode.executeUncached(arg2, false);
+ Object result = CallSlotNbInPlacePowerNode.executeUncached(self.getSlot(), v, w, z);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "NbInPlacePowerWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new NbInPlacePowerWrapper(slot);
+ }
+ }
+
+ public static final class RichcmpFunctionWrapper extends TpSlotWrapper {
+
+ public RichcmpFunctionWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PPI, HANDLE_RICHCMP_FUNCTION);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeRichcmpFunction(RichcmpFunctionWrapper self, long arg0, long arg1, int arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ // convert args
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object jArg1 = NativeToPythonInternalNode.executeUncached(arg1, false);
+ RichCmpOp op = RichCmpOp.fromNative(arg2);
+ Object result = CallSlotRichCmpNode.executeUncached(self.getSlot(), jArg0, jArg1, op);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "RichcmpFunctionWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new RichcmpFunctionWrapper(slot);
+ }
+ }
+
+ public static final class SsizeargfuncSlotWrapper extends TpSlotWrapper {
+
+ public SsizeargfuncSlotWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PL, HANDLE_SSIZEARGFUNC_SLOT);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeSsizeargfuncSlot(SsizeargfuncSlotWrapper self, long arg0, long arg1) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ int index = ssizeAsIntUncached(arg1);
+ Object result = CallSlotSizeArgFun.executeUncached(self.getSlot(), jArg0, index);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "SsizeargfuncWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new SsizeargfuncSlotWrapper(slot);
+ }
+ }
+
+ /**
+ * For the time being when indices/lengths in GraalPy are 32bit integers, we must deal with
+ * possible situation that someone passes larger number to us. In long term, we should migrate
+ * indices/length to use longs.
+ */
+ @TruffleBoundary
+ private static int ssizeAsIntUncached(long l) {
+ if (PInt.isIntRange(l)) {
+ return (int) l;
+ }
+ throw PRaiseNode.raiseStatic(null, PythonBuiltinClassType.IndexError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, l);
+ }
+
+ public static final class SsizeobjargprocWrapper extends TpSlotWrapper {
+
+ public SsizeobjargprocWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_I_PLP, HANDLE_SSIZEOBJARGPROC);
+ }
+
+ @SuppressWarnings("try")
+ private static int executeSsizeobjargproc(SsizeobjargprocWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ int key = ssizeAsIntUncached(arg1);
+ Object jArg2 = NativeToPythonInternalNode.executeUncached(arg2, false);
+ CallSlotSqAssItemNode.executeUncached(self.getSlot(), jArg0, key, jArg2);
+ return 0;
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "SsizeobjargprocWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new SsizeobjargprocWrapper(slot);
+ }
+ }
+
+ public static final class LenfuncWrapper extends TpSlotWrapper {
+ public LenfuncWrapper(TpSlotManaged managedSlot) {
+ super(managedSlot, SIGNATURE_L_P, HANDLE_LENFUNC);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeLenfunc(LenfuncWrapper self, long arg0) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ return CallSlotLenNode.executeUncached(self.getSlot(), jArg0);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "LenfuncWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new LenfuncWrapper(slot);
+ }
+ }
+
+ public static final class HashfuncWrapper extends TpSlotWrapper {
+
+ public HashfuncWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_L_P, HANDLE_HASHFUNC);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeHashfunc(HashfuncWrapper self, long arg0) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ Object jArg0 = NativeToPythonInternalNode.executeUncached(arg0, false);
+ return CallSlotHashFunNode.executeUncached(self.getSlot(), jArg0);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "HashfuncWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return -1;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new HashfuncWrapper(slot);
+ }
+ }
+
+ public static final class DescrGetFunctionWrapper extends TpSlotWrapper {
+ public DescrGetFunctionWrapper(TpSlotManaged slot) {
+ super(slot, SIGNATURE_P_PPP, HANDLE_DESCR_GET_FUNCTION);
+ }
+
+ @SuppressWarnings("try")
+ private static long executeDescrGetFunction(DescrGetFunctionWrapper self, long arg0, long arg1, long arg2) {
+ try (var gil = GilNode.uncachedAcquire()) {
+ CApiTiming.enter();
+ try {
+ // convert args
+ Object receiver = NativeToPythonInternalNode.executeUncached(arg0, false);
+ Object obj = NativeToPythonInternalNode.executeUncached(arg1, false);
+ Object cls = NativeToPythonInternalNode.executeUncached(arg2, false);
+ Object result = CallSlotDescrGet.executeUncached(self.getSlot(), receiver, obj, cls);
+ return PythonToNativeNewRefNode.executeLongUncached(result);
+ } catch (Throwable t) {
+ throw checkThrowableBeforeNative(t, "DescrGetFunctionWrapper", self.getSlot());
+ }
+ } catch (PException e) {
+ TransformExceptionToNativeNode.executeUncached(e.getEscapedException());
+ return NULLPTR;
+ } finally {
+ CApiTiming.exit(self.timing);
+ }
+ }
+
+ @Override
+ public TpSlotWrapper cloneWith(TpSlotManaged slot) {
+ return new DescrGetFunctionWrapper(slot);
+ }
+ }
+
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TruffleObjectNativeWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TruffleObjectNativeWrapper.java
index e833dd17cc..b1a2a82e7b 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TruffleObjectNativeWrapper.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TruffleObjectNativeWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2024, 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
@@ -40,39 +40,19 @@
*/
package com.oracle.graal.python.builtins.objects.cext.capi;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
+import com.oracle.graal.python.builtins.objects.object.PythonObject;
+import com.oracle.truffle.api.object.Shape;
-@ExportLibrary(InteropLibrary.class)
-public final class TruffleObjectNativeWrapper extends PythonAbstractObjectNativeWrapper {
+public final class TruffleObjectNativeWrapper extends PythonObject {
- public TruffleObjectNativeWrapper(Object foreignObject) {
- super(foreignObject);
- }
-
- public static TruffleObjectNativeWrapper wrap(Object foreignObject) {
- assert foreignObject != null : "attempting to wrap Java null";
- assert !CApiGuards.isNativeWrapper(foreignObject) : "attempting to wrap a native wrapper";
- return new TruffleObjectNativeWrapper(foreignObject);
- }
-
- @ExportMessage
- boolean isPointer() {
- return isNative();
- }
+ private final Object foreignObject;
- @ExportMessage
- long asPointer() {
- return getNativePointer();
+ public TruffleObjectNativeWrapper(Object pythonClass, Shape instanceShape, Object foreignObject) {
+ super(pythonClass, instanceShape);
+ this.foreignObject = foreignObject;
}
- @ExportMessage
- void toNative() {
- if (!isNative()) {
- setNativePointer(CApiTransitions.FirstToNativeNode.executeUncached(this, false));
- }
+ public Object getForeignObject() {
+ return foreignObject;
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java
index 666725a32f..5414762822 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2026, 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
@@ -43,57 +43,53 @@
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.FromLongNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ToNativeBorrowedNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ToPythonStringNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.CheckInquiryResultNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.CheckIterNextResultNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.CheckPrimitiveFunctionResultNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodesFactory.InitCheckFunctionResultNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonClassNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonReturnNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.ToPythonWrapperNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.WrappedPointerToPythonNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtToJavaNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtToNativeNode;
+import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType;
import com.oracle.graal.python.util.Supplier;
enum ArgBehavior {
PyObject(
- "POINTER",
- "J",
- "jlong",
- "long",
+ NativeSimpleType.RAW_POINTER,
PythonToNativeNode::create,
NativeToPythonNode::create,
NativeToPythonNode.getUncached(),
PythonToNativeNewRefNode::create,
NativeToPythonTransferNode::create,
NativeToPythonTransferNode.getUncached()),
- PyObjectBorrowed("POINTER", "J", "jlong", "long", ToNativeBorrowedNode::new, NativeToPythonNode::create, NativeToPythonNode.getUncached(), null, null, null),
- PyObjectAsTruffleString("POINTER", "J", "jlong", "long", null, ToPythonStringNode::create, ToPythonStringNode.getUncached(), null, null, null),
- PyObjectWrapper("POINTER", "J", "jlong", "long", null, ToPythonWrapperNode::create, ToPythonWrapperNode.getUncached(), null, null, null),
- Pointer("POINTER", "J", "jlong", "long", null, null, null),
- WrappedPointer("POINTER", "J", "jlong", "long", null, WrappedPointerToPythonNodeGen::create, WrappedPointerToPythonNodeGen.getUncached()),
- TruffleStringPointer("POINTER", "J", "jlong", "long", null, CharPtrToPythonNode::create, CharPtrToPythonNode.getUncached()),
- Char8("SINT8", "C", "jbyte", "byte", null, null, null),
- UChar8("UINT8", "C", "jbyte", "byte", null, null, null),
- Char16("SINT16", "C", "jchar", "char", null, null, null),
- Int32("SINT32", "I", "jint", "int", null, null, null),
- UInt32("UINT32", "I", "jint", "int", null, null, null),
- Int64("SINT64", "J", "jlong", "long", null, null, null),
- UInt64("UINT64", "J", "jlong", "long", null, null, null),
- Long("SINT64", "J", "jlong", "long", null, FromLongNode::create, FromLongNode.getUncached()),
- Float32("FLOAT", "F", "jfloat", "float", null, null, null),
- Float64("DOUBLE", "D", "jdouble", "double", null, null, null),
- Void("VOID", "V", "void", "void", null, null, null),
- Unknown("SINT64", "J", "jlong", "long", null, null, null);
-
- public final String nfiSignature;
- public final String jniSignature;
- public final String jniType;
- public final String javaSignature;
+ PyObjectBorrowed(NativeSimpleType.RAW_POINTER, ToNativeBorrowedNode::new, NativeToPythonNode::create, NativeToPythonNode.getUncached(), null, null, null),
+ PyObjectAsTruffleString(NativeSimpleType.RAW_POINTER, null, ToPythonStringNode::create, ToPythonStringNode.getUncached(), null, null, null),
+ PyTypeObject(
+ NativeSimpleType.RAW_POINTER,
+ PythonToNativeNode::create,
+ NativeToPythonClassNode::create,
+ NativeToPythonClassNode.getUncached(),
+ PythonToNativeNewRefNode::create,
+ NativeToPythonTransferNode::create,
+ NativeToPythonTransferNode.getUncached()),
+ Pointer(NativeSimpleType.RAW_POINTER),
+ TruffleStringPointer(NativeSimpleType.RAW_POINTER, null, CharPtrToPythonNode::create, CharPtrToPythonNode.getUncached()),
+ Char8(NativeSimpleType.SINT8),
+ UChar8(NativeSimpleType.SINT8),
+ Char16(NativeSimpleType.SINT16),
+ Int32(NativeSimpleType.SINT32),
+ UInt32(NativeSimpleType.SINT32),
+ Int64(NativeSimpleType.SINT64),
+ UInt64(NativeSimpleType.SINT64),
+ Long(NativeSimpleType.SINT64, null, FromLongNode::create, FromLongNode.getUncached()),
+ Float32(NativeSimpleType.FLOAT),
+ Float64(NativeSimpleType.DOUBLE),
+ Void(NativeSimpleType.VOID),
+ Unknown(NativeSimpleType.SINT64);
+
+ public final NativeSimpleType nativeSimpleType;
public final Supplier pythonToNative;
public final Supplier nativeToPython;
public final CExtToJavaNode uncachedNativeToPython;
@@ -101,13 +97,9 @@ enum ArgBehavior {
public final Supplier nativeToPythonTransfer;
public final CExtToJavaNode uncachedNativeToPythonTransfer;
- ArgBehavior(String nfiSignature, String jniSignature, String jniType, String javaSignature, Supplier pythonToNative, Supplier nativeToPython,
- CExtToJavaNode uncachedNativeToPython,
+ ArgBehavior(NativeSimpleType nativeSimpleType, Supplier pythonToNative, Supplier nativeToPython, CExtToJavaNode uncachedNativeToPython,
Supplier pythonToNativeTransfer, Supplier nativeToPythonTransfer, CExtToJavaNode uncachedNativeToPythonTransfer) {
- this.nfiSignature = nfiSignature;
- this.jniSignature = jniSignature;
- this.jniType = jniType;
- this.javaSignature = javaSignature;
+ this.nativeSimpleType = nativeSimpleType;
this.pythonToNative = pythonToNative;
this.nativeToPython = nativeToPython;
this.uncachedNativeToPython = uncachedNativeToPython;
@@ -116,10 +108,14 @@ enum ArgBehavior {
this.uncachedNativeToPythonTransfer = uncachedNativeToPythonTransfer;
}
- ArgBehavior(String nfiSignature, String jniSignature, String jniType, String javaType, Supplier pythonToNative, Supplier nativeToPython,
- CExtToJavaNode uncachedNativeToPython) {
- this(nfiSignature, jniSignature, jniType, javaType, pythonToNative, nativeToPython, uncachedNativeToPython, null, null, null);
+ ArgBehavior(NativeSimpleType nativeSimpleType, Supplier pythonToNative, Supplier nativeToPython, CExtToJavaNode uncachedNativeToPython) {
+ this(nativeSimpleType, pythonToNative, nativeToPython, uncachedNativeToPython, null, null, null);
}
+
+ ArgBehavior(NativeSimpleType nativeSimpleType) {
+ this(nativeSimpleType, null, null, null, null, null, null);
+ }
+
}
public enum ArgDescriptor {
@@ -127,16 +123,17 @@ public enum ArgDescriptor {
VoidNoReturn(ArgBehavior.Void, "void"),
PyObject(ArgBehavior.PyObject, "PyObject*"),
PyObjectBorrowed(ArgBehavior.PyObjectBorrowed, "PyObject*"),
- PyObjectWrapper(ArgBehavior.PyObjectWrapper, "PyObject*"),
PyObjectAsTruffleString(ArgBehavior.PyObjectAsTruffleString, "PyObject*"),
- PyTypeObject(ArgBehavior.PyObject, "PyTypeObject*"),
+ PyTypeObject(ArgBehavior.PyTypeObject, "PyTypeObject*"),
PyTypeObjectBorrowed(ArgBehavior.PyObjectBorrowed, "PyTypeObject*"),
- PyTypeObjectTransfer(ArgBehavior.PyObject, "PyTypeObject*", true),
+ PyTypeObjectTransfer(ArgBehavior.PyObject, "PyTypeObject*", true, false),
+ PyTypeObjectRawPointer(ArgBehavior.Pointer, "PyTypeObject*"),
PyListObject(ArgBehavior.PyObject, "PyListObject*"),
PyTupleObject(ArgBehavior.PyObject, "PyTupleObject*"),
PyMethodObject(ArgBehavior.PyObject, "PyMethodObject*"),
PyInstanceMethodObject(ArgBehavior.PyObject, "PyInstanceMethodObject*"),
- PyObjectTransfer(ArgBehavior.PyObject, "PyObject*", true),
+ PyObjectTransfer(ArgBehavior.PyObject, "PyObject*", true, false),
+ PyObjectReturn(ArgBehavior.PyObject, "PyObject*", true, true),
PyObjectRawPointer(ArgBehavior.Pointer, "PyObject*"),
Pointer(ArgBehavior.Pointer, "void*"),
Py_ssize_t(ArgBehavior.Int64, "Py_ssize_t"),
@@ -146,6 +143,7 @@ public enum ArgDescriptor {
Double(ArgBehavior.Float64, "double"),
Float(ArgBehavior.Float32, "float"),
Long(ArgBehavior.Long, "long"),
+ PyObjectConstArray(ArgBehavior.Pointer, "PyObject *const *"),
_FRAME(ArgBehavior.PyObject, "struct _frame*"),
_MOD_PTR("struct _mod*"),
@@ -205,7 +203,8 @@ public enum ArgDescriptor {
PyCMethodObject(ArgBehavior.PyObject, "PyCMethodObject*"),
PY_CAPSULE_DESTRUCTOR(ArgBehavior.Pointer, "PyCapsule_Destructor"),
PyCodeObject(ArgBehavior.PyObject, "PyCodeObject*"),
- PyCodeObjectTransfer(ArgBehavior.PyObject, "PyCodeObject*", true),
+ PyCodeObjectTransfer(ArgBehavior.PyObject, "PyCodeObject*", true, false),
+ PyCodeObjectRawPointer(ArgBehavior.Pointer, "PyCodeObject*"),
PyCode_WatchCallback(ArgBehavior.Pointer, "PyCode_WatchCallback"),
PY_COMPILER_FLAGS(ArgBehavior.Pointer, "PyCompilerFlags*"),
PY_COMPLEX("Py_complex"),
@@ -215,7 +214,8 @@ public enum ArgDescriptor {
PyFrameConstructor("PyFrameConstructor*"),
PyFrameObject(ArgBehavior.PyObject, "PyFrameObject*"),
PyFrameObjectBorrowed(ArgBehavior.PyObjectBorrowed, "PyFrameObject*"),
- PyFrameObjectTransfer(ArgBehavior.PyObject, "PyFrameObject*", true),
+ PyFrameObjectTransfer(ArgBehavior.PyObject, "PyFrameObject*", true, false),
+ PyFrameObjectRawPointer(ArgBehavior.Pointer, "PyFrameObject*"),
_PyFrameEvalFunction("_PyFrameEvalFunction"),
_PyInterpreterFrame("struct _PyInterpreterFrame*"),
PY_GEN_OBJECT(ArgBehavior.PyObject, "PyGenObject*"),
@@ -228,12 +228,13 @@ public enum ArgDescriptor {
PY_LOCK_STATUS("PyLockStatus"),
PyLongObject(ArgBehavior.PyObject, "PyLongObject*"),
ConstPyLongObject(ArgBehavior.PyObject, "const PyLongObject*"),
- PyLongObjectTransfer(ArgBehavior.PyObject, "PyLongObject*", true),
+ PyLongObjectTransfer(ArgBehavior.PyObject, "PyLongObject*", true, false),
PyMemberDef(ArgBehavior.Pointer, "PyMemberDef*"),
PyModuleObject(ArgBehavior.PyObject, "PyModuleObject*"),
- PyModuleObjectTransfer(ArgBehavior.PyObject, "PyModuleObject*", true),
- PyMethodDef(ArgBehavior.WrappedPointer, "PyMethodDef*"),
- PyModuleDef(ArgBehavior.Pointer, "PyModuleDef*"), // it's unclear if this should be PyObject
+ PyModuleObjectTransfer(ArgBehavior.PyObject, "PyModuleObject*", true, false),
+ PyMethodDef(ArgBehavior.Pointer, "PyMethodDef*"),
+ PyModuleDef(ArgBehavior.Pointer, "PyModuleDef*"), // it's unclear if this should be
+ // PyObject
PyModuleDefSlot(ArgBehavior.Pointer, "PyModuleDef_Slot*"),
PyNumberMethods(ArgBehavior.Pointer, "PyNumberMethods*"),
PySequenceMethods(ArgBehavior.Pointer, "PySequenceMethods*"),
@@ -261,7 +262,7 @@ public enum ArgDescriptor {
PY_UCS4_PTR("Py_UCS4*"),
PY_UNICODE("Py_UNICODE"),
PyUnicodeObject(ArgBehavior.PyObject, "PyUnicodeObject*"),
- PY_UNICODE_PTR(ArgBehavior.WrappedPointer, "Py_UNICODE*"),
+ PY_UNICODE_PTR(ArgBehavior.Pointer, "Py_UNICODE*"),
PyVarObject(ArgBehavior.PyObject, "PyVarObject*"),
ConstPyVarObject(ArgBehavior.PyObject, "const PyVarObject*"),
PYADDRPAIR_PTR("PyAddrPair*"),
@@ -280,7 +281,7 @@ public enum ArgDescriptor {
PYPRECONFIG_PTR("PyPreConfig*"),
PYSTATUS("PyStatus"),
PYUNICODE_KIND("enum PyUnicode_Kind"),
- PYWEAKREFERENCE_PTR(ArgBehavior.PyObject, "PyWeakReference*"),
+ PYWEAKREFERENCE_PTR(ArgBehavior.Pointer, "PyWeakReference*"),
PYWIDESTRINGLIST_PTR("PyWideStringList*"),
PyDict_WatchCallback(ArgBehavior.Pointer, "PyDict_WatchCallback"),
PyFunction_WatchCallback(ArgBehavior.Pointer, "PyFunction_WatchCallback"),
@@ -353,56 +354,33 @@ public enum ArgDescriptor {
xid_newobjectfunc(ArgBehavior.Pointer, "xid_newobjectfunc"),
atexit_datacallbackfunc(ArgBehavior.Pointer, "atexit_datacallbackfunc"),
- IterResult(ArgBehavior.PyObject, "void*", CheckIterNextResultNodeGen::create, CheckIterNextResultNodeGen.getUncached(), true),
- InquiryResult(ArgBehavior.Int32, "int", CheckInquiryResultNodeGen::create, CheckInquiryResultNodeGen.getUncached()),
- InitResult(ArgBehavior.Int32, "int", InitCheckFunctionResultNodeGen::create, InitCheckFunctionResultNodeGen.getUncached()),
- PrimitiveResult32(ArgBehavior.Int32, "int", CheckPrimitiveFunctionResultNodeGen::create, CheckPrimitiveFunctionResultNodeGen.getUncached()),
- PrimitiveResult64(ArgBehavior.Int64, "long", CheckPrimitiveFunctionResultNodeGen::create, CheckPrimitiveFunctionResultNodeGen.getUncached());
+ PrimitiveResult32(ArgBehavior.Int32, "int"),
+ PrimitiveResult64(ArgBehavior.Int64, "long");
private final String cSignature;
private final ArgBehavior behavior;
private final boolean transfer;
- private final Supplier checkResult;
- private final CheckFunctionResultNode uncachedCheckResult;
+ private final boolean release;
ArgDescriptor(String cSignature) {
this.behavior = ArgBehavior.Unknown;
this.cSignature = cSignature;
this.transfer = false;
- this.checkResult = null;
- this.uncachedCheckResult = null;
+ this.release = false;
}
ArgDescriptor(ArgBehavior behavior, String cSignature) {
this.behavior = behavior;
this.cSignature = cSignature;
this.transfer = false;
- this.checkResult = null;
- this.uncachedCheckResult = null;
- }
-
- ArgDescriptor(ArgBehavior behavior, String cSignature, boolean transfer) {
- this.behavior = behavior;
- this.cSignature = cSignature;
- this.transfer = transfer;
- this.checkResult = null;
- this.uncachedCheckResult = null;
- }
-
- ArgDescriptor(ArgBehavior behavior, String cSignature, Supplier checkResult, CheckFunctionResultNode uncachedCheckResult) {
- this.behavior = behavior;
- this.cSignature = cSignature;
- this.checkResult = checkResult;
- this.uncachedCheckResult = uncachedCheckResult;
- this.transfer = false;
+ this.release = false;
}
- ArgDescriptor(ArgBehavior behavior, String cSignature, Supplier checkResult, CheckFunctionResultNode uncachedCheckResult, boolean transfer) {
+ ArgDescriptor(ArgBehavior behavior, String cSignature, boolean transfer, boolean release) {
this.behavior = behavior;
this.cSignature = cSignature;
- this.checkResult = checkResult;
- this.uncachedCheckResult = uncachedCheckResult;
this.transfer = transfer;
+ this.release = release;
}
public static CExtToJavaNode[] createNativeToPython(ArgDescriptor[] args) {
@@ -426,7 +404,14 @@ public CExtToNativeNode createPythonToNativeNode() {
public CExtToJavaNode createNativeToPythonNode() {
assert behavior != ArgBehavior.Unknown : "undefined behavior in " + this;
- Supplier factory = transfer ? behavior.nativeToPythonTransfer : behavior.nativeToPython;
+ Supplier factory;
+ if (transfer && release) {
+ factory = NativeToPythonReturnNode::create;
+ } else if (transfer) {
+ factory = behavior.nativeToPythonTransfer;
+ } else {
+ factory = behavior.nativeToPython;
+ }
assert !(transfer && factory == null);
return factory == null ? null : factory.get();
}
@@ -438,80 +423,35 @@ public CExtToJavaNode getUncachedNativeToPythonNode() {
return node;
}
- public CheckFunctionResultNode createCheckResultNode() {
- assert behavior != ArgBehavior.Unknown : "undefined behavior in " + this;
- return checkResult == null ? null : checkResult.get();
- }
-
- public CheckFunctionResultNode getUncachedCheckResultNode() {
- assert behavior != ArgBehavior.Unknown : "undefined behavior in " + this;
- return uncachedCheckResult;
- }
-
- public String getNFISignature() {
- return behavior.nfiSignature;
- }
-
- public String getJniSignature() {
- return behavior.jniSignature;
- }
-
- public String getJniType() {
- return behavior.jniType;
- }
-
- public String getJavaSignature() {
- return behavior.javaSignature;
+ public NativeSimpleType getNativeSimpleType() {
+ return behavior.nativeSimpleType;
}
public boolean isPyObjectOrPointer() {
- return behavior == ArgBehavior.PyObject || behavior == ArgBehavior.PyObjectBorrowed || behavior == ArgBehavior.Pointer || behavior == ArgBehavior.WrappedPointer ||
- behavior == ArgBehavior.TruffleStringPointer;
+ return switch (behavior) {
+ case PyObject, PyTypeObject, PyObjectBorrowed, Pointer, TruffleStringPointer -> true;
+ default -> false;
+ };
}
public boolean isPointer() {
- return behavior == ArgBehavior.Pointer || behavior == ArgBehavior.WrappedPointer || behavior == ArgBehavior.TruffleStringPointer;
+ return switch (behavior) {
+ case Pointer, TruffleStringPointer -> true;
+ default -> false;
+ };
}
public boolean isPyObject() {
- return behavior == ArgBehavior.PyObject || behavior == ArgBehavior.PyObjectBorrowed;
- }
-
- public boolean isValidReturnType() {
- /*
- * We don't want to allow "bare" PyObject and force ourselves to decide between
- * PyObjectTransfer and PyObjectBorrow
- */
- return behavior != ArgBehavior.PyObject || transfer;
+ return switch (behavior) {
+ case PyObject, PyObjectBorrowed, PyTypeObject -> true;
+ default -> false;
+ };
}
public boolean isCharPtr() {
return this == CharPtrAsTruffleString || this == CHAR_PTR || this == ConstCharPtr || this == ConstCharPtrAsTruffleString;
}
- public boolean isIntType() {
- switch (behavior) {
- case Int32:
- case UInt32:
- case Int64:
- case UInt64:
- case Long:
- case Char16:
- case Unknown:
- return true;
- default:
- return false;
- }
- }
-
- public boolean isFloatType() {
- return behavior == ArgBehavior.Float64 || behavior == ArgBehavior.Float32;
- }
-
- public boolean isVoid() {
- return behavior == ArgBehavior.Void;
- }
-
public boolean isI64() {
return behavior == ArgBehavior.Int64 || behavior == ArgBehavior.Long || behavior == ArgBehavior.UInt64;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/CApiTransitions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/CApiTransitions.java
index dac761033b..871903525f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/CApiTransitions.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/CApiTransitions.java
@@ -40,14 +40,26 @@
*/
package com.oracle.graal.python.builtins.objects.cext.capi.transitions;
-import static com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper.IMMORTAL_REFCNT;
-import static com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper.MANAGED_REFCNT;
+import static com.oracle.graal.python.builtins.objects.PythonAbstractObject.NATIVE_POINTER_FREED;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PollingState.RQ_DISABLED_PERMANENT;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PollingState.RQ_DISABLED_TEMP;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PollingState.RQ_POLLING;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PollingState.RQ_READY;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PollingState.RQ_UNINITIALIZED;
-
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeDoubleField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writePtrField;
+import static com.oracle.graal.python.builtins.objects.object.PythonObject.IMMORTAL_REFCNT;
+import static com.oracle.graal.python.builtins.objects.object.PythonObject.MANAGED_REFCNT;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.calloc;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.free;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.mallocPtrArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writePtrArrayElements;
+
+import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -67,50 +79,56 @@
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
+import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiGCSupport.GCListRemoveNode;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiGCSupport.PyObjectGCDelNode;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiGCSupport.PyObjectGCTrackNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.CApiGuards;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
-import com.oracle.graal.python.builtins.objects.cext.capi.PrimitiveNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonClassNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.TruffleObjectNativeWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.PyMemoryViewWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.AllocateNativeObjectStubNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.FirstToNativeNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.NativePtrToPythonNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.NativeToPythonClassInternalNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.NativeToPythonClassNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.NativeToPythonInternalNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.NativeToPythonNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.NativeToPythonReturnNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.NativeToPythonTransferNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeInternalNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNewRefNodeGen;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNodeGen;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CoerceNativePointerToLongNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtToJavaNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtToNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.HandleStack;
-import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.AllocateNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.FreeNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
import com.oracle.graal.python.builtins.objects.floats.PFloat;
import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorDeleteMarker;
import com.oracle.graal.python.builtins.objects.ints.PInt;
+import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView;
import com.oracle.graal.python.builtins.objects.object.PythonObject;
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
+import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass;
+import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
+import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
import com.oracle.graal.python.builtins.objects.type.TypeFlags;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes;
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetTypeFlagsNode;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
+import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode;
import com.oracle.graal.python.runtime.GilNode;
import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage;
@@ -139,7 +157,6 @@
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
import com.oracle.truffle.api.profiles.InlinedExactClassProfile;
-import com.oracle.truffle.api.strings.TruffleString;
import sun.misc.Unsafe;
@@ -177,6 +194,11 @@ private CApiTransitions() {
// transfer: steal or borrow reference
public static final class HandleContext {
+ /**
+ * Never use handle table index '0' to avoid that zeroed memory accidentally maps to some
+ * valid object.
+ */
+ private static final int FIRST_VALID_INDEX = 1;
private static final int DEFAULT_CAPACITY = 16;
/** Threshold used to switch from exponential to linear growth. */
@@ -184,11 +206,10 @@ public static final class HandleContext {
public HandleContext(boolean useShadowTable) {
nativeStubLookupShadowTable = useShadowTable ? new HashMap<>() : null;
- nativeStubLookup = new PythonObjectReference[DEFAULT_CAPACITY];
+ nativeStubLookup = new Object[DEFAULT_CAPACITY];
nativeStubLookupFreeStack = new HandleStack(DEFAULT_CAPACITY);
- // Never use 'handleTableIndex == 0' to avoid that zeroed memory
- // accidentally maps to some valid object.
- nativeStubLookupFreeStack.pushRange(1, DEFAULT_CAPACITY);
+ nativeStubLookupFreeStack.pushRange(FIRST_VALID_INDEX, DEFAULT_CAPACITY);
+ nativeTypeLookup = new IdReference>[DEFAULT_CAPACITY];
}
public final ArrayList referencesToBeFreed = new ArrayList<>();
@@ -196,8 +217,10 @@ public HandleContext(boolean useShadowTable) {
public final ConcurrentHashMap nativeWeakRef = new ConcurrentHashMap<>();
public final WeakHashMap> managedNativeLookup = new WeakHashMap<>();
- private final HashMap nativeStubLookupShadowTable;
- public PythonObjectReference[] nativeStubLookup;
+ public IdReference>[] nativeTypeLookup;
+
+ private final HashMap nativeStubLookupShadowTable;
+ public Object[] nativeStubLookup;
public final HandleStack nativeStubLookupFreeStack;
public final Set nativeStorageReferences = new HashSet<>();
@@ -217,6 +240,16 @@ public static T removeShadowTable(HashMap table, long pointer) {
return table.remove(pointer);
}
+ @TruffleBoundary
+ public static T removeShadowTable(HashMap table, Object refOrWrapper) {
+ if (refOrWrapper instanceof PythonObjectReference ref) {
+ return table.remove(ref.pointer);
+ } else if (refOrWrapper instanceof PythonObject pythonObject) {
+ return table.remove(pythonObject.getNativePointer());
+ }
+ throw CompilerDirectives.shouldNotReachHere("Handle table must contain PythonObjectReference or PythonAbstractObjectNativeWrapper");
+ }
+
@TruffleBoundary
public static T getShadowTable(HashMap table, long pointer) {
return table.get(pointer);
@@ -224,7 +257,7 @@ public static T getShadowTable(HashMap table, long pointer) {
}
private static HandleContext getContext() {
- return PythonContext.get(null).nativeContext;
+ return PythonContext.get(null).handleContext;
}
public abstract static class IdReference extends WeakReference {
@@ -238,12 +271,12 @@ public IdReference(HandleContext handleContext, T referent) {
/**
* A weak and unique reference to a native wrapper of a reference counted managed object.
*/
- public static final class PythonObjectReference extends IdReference {
+ public static final class PythonObjectReference extends IdReference {
/**
* This reference forces the wrapper to remain alive, and can be set to null when the
- * refcount falls to {@link PythonAbstractObjectNativeWrapper#MANAGED_REFCNT}.
+ * refcount falls to {@link PythonObject#MANAGED_REFCNT}.
*/
- private PythonNativeWrapper strongReference;
+ private PythonObject strongReference;
private final long pointer;
/**
@@ -257,17 +290,17 @@ public static final class PythonObjectReference extends IdReference= 0;
return new PythonObjectReference(handleContext, referent, strong, pointer, idx, true, gc);
}
- static PythonObjectReference createReplacement(HandleContext handleContext, PythonNativeWrapper referent, long pointer, boolean allocatedFromJava) {
+ static PythonObjectReference createReplacement(HandleContext handleContext, PythonObject referent, long pointer, boolean allocatedFromJava) {
assert !HandlePointerConverter.pointsToPyHandleSpace(pointer);
return new PythonObjectReference(handleContext, referent, true, pointer, -1, allocatedFromJava, false);
}
- public boolean isStubReference() {
- return handleTableIndex >= 0;
- }
-
public boolean isStrongReference() {
return strongReference != null;
}
- public void setStrongReference(PythonNativeWrapper wrapper) {
- strongReference = wrapper;
+ public void setStrongReference(PythonObject object) {
+ strongReference = object;
}
public int getHandleTableIndex() {
@@ -323,7 +352,7 @@ public boolean isAllocatedFromJava() {
@TruffleBoundary
public String toString() {
String type = strongReference != null ? "strong" : "weak";
- PythonNativeWrapper referent = get();
+ PythonObject referent = get();
return String.format("PythonObjectReference<0x%x,%s,%s,id=%d>", pointer, type, referent != null ? referent : "freed", handleTableIndex);
}
}
@@ -334,12 +363,10 @@ public String toString() {
*/
public static final class NativeObjectReference extends IdReference {
- final Object object;
final long pointer;
public NativeObjectReference(HandleContext handleContext, PythonAbstractNativeObject referent, long pointer) {
super(handleContext, referent);
- this.object = referent.object;
this.pointer = pointer;
referent.ref = this;
assert (pointer & 7) == 0;
@@ -367,7 +394,7 @@ public String toString() {
public static final class NativeStorageReference extends IdReference {
private final SequenceStorage.StorageType type;
- private Object ptr;
+ private long ptr;
private int size;
public NativeStorageReference(HandleContext handleContext, NativeSequenceStorage storage) {
@@ -384,7 +411,7 @@ public Object getPtr() {
return ptr;
}
- public void setPtr(Object ptr) {
+ public void setPtr(long ptr) {
this.ptr = ptr;
}
@@ -449,7 +476,7 @@ public static PyCapsuleReference registerPyCapsuleDestructor(PyCapsule capsule)
@SuppressWarnings("try")
public static int pollReferenceQueue() {
PythonContext context = PythonContext.get(null);
- HandleContext handleContext = context.nativeContext;
+ HandleContext handleContext = context.handleContext;
int manuallyCollected = 0;
if (handleContext.referenceQueuePollingState != RQ_READY) {
return manuallyCollected;
@@ -524,7 +551,7 @@ public static int pollReferenceQueue() {
* to avoid incorrect reuse of the ID which could resolve to another
* object.
*/
- CStructAccess.WriteIntNode.writeUncached(stubPointer, CFields.GraalPyObject__handle_table_index, 0);
+ writeIntField(stubPointer, CFields.GraalPyObject__handle_table_index, 0);
// this can only happen if the object is a GC object
assert reference.gc;
/*
@@ -556,7 +583,7 @@ public static int pollReferenceQueue() {
}
}
} else if (entry instanceof NativeObjectReference reference) {
- if (nativeLookupRemove(handleContext, reference.pointer) != null) {
+ if (nativeLookupRemove(handleContext, reference.pointer) == reference) {
// The reference was still in our lookup table, it was not otherwise
// freed and we can process it now
LOGGER.finer(() -> PythonUtils.formatJString("releasing native lookup for native object %x => %s", reference.pointer, reference));
@@ -579,10 +606,9 @@ public static int pollReferenceQueue() {
}
/**
- * Subtracts {@link PythonAbstractObjectNativeWrapper#MANAGED_REFCNT} from the object's
- * reference count and if it is then {@code 0}, it puts the pointer into the list of references
- * to be freed. Therefore, this method neither frees any native memory nor runs any object
- * destructor (guest code).
+ * Subtracts {@link PythonObject#MANAGED_REFCNT} from the object's reference count and if it is
+ * then {@code 0}, it puts the pointer into the list of references to be freed. Therefore, this
+ * method neither frees any native memory nor runs any object destructor (guest code).
*/
private static void processNativeObjectReference(NativeObjectReference reference, ArrayList referencesToBeFreed) {
LOGGER.fine(() -> PythonUtils.formatJString("releasing %s", reference.toString()));
@@ -603,7 +629,13 @@ private static void processNativeStorageReference(NativeStorageReference referen
* GC.
*/
if (reference.type == StorageType.Generic && reference.size > 0) {
- PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_OBJECT_ARRAY_RELEASE, reference.ptr, reference.size);
+ try {
+ ExternalFunctionInvoker.invokeOBJECT_ARRAY_RELEASE(
+ CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_OBJECT_ARRAY_RELEASE).getAddress(), reference.ptr,
+ reference.size);
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
}
assert !InteropLibrary.getUncached().isNull(reference.ptr);
freeNativeStorage(reference);
@@ -611,10 +643,20 @@ private static void processNativeStorageReference(NativeStorageReference referen
private static void processPyCapsuleReference(PyCapsuleReference reference) {
LOGGER.fine(() -> PythonUtils.formatJString("releasing %s", reference.toString()));
- if (reference.data.getDestructor() != null) {
+ if (reference.data.getDestructor() != NULLPTR) {
// Our capsule is dead, so create a temporary copy that doesn't have a reference anymore
PyCapsule capsule = PFactory.createCapsule(PythonLanguage.get(null), reference.data);
- PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_GRAALPY_CAPSULE_CALL_DESTRUCTOR, PythonToNativeNode.executeUncached(capsule), capsule.getDestructor());
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(capsule);
+ long capsulePointer = PythonToNativeNode.executeLongUncached(capsule);
+ try {
+ ExternalFunctionInvoker.invokeGRAALPY_CAPSULE_CALL_DESTRUCTOR(
+ CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_GRAALPY_CAPSULE_CALL_DESTRUCTOR).getAddress(),
+ capsulePointer, capsule.getDestructor());
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ } finally {
+ Reference.reachabilityFence(capsule);
+ }
}
}
@@ -631,6 +673,8 @@ private static void releaseNativeObjects(PythonContext context, ArrayList
*/
assert context.ownsGil();
PythonContext.PythonThreadState threadState = context.getThreadState(context.getLanguage());
+ // at this point, the thread state must not have been free'd
+ assert threadState.getNativePointer() != NATIVE_POINTER_FREED;
/*
* There can be an active exception. Since we might be calling arbitary python, we need
* to stash it.
@@ -639,12 +683,17 @@ private static void releaseNativeObjects(PythonContext context, ArrayList
try {
int size = referencesToBeFreed.size();
LOGGER.fine(() -> PythonUtils.formatJString("releasing %d NativeObjectReference instances", size));
- long pointer = AllocateNode.allocUncachedPointer(size * Long.BYTES);
+ long pointer = mallocPtrArray(size);
for (int i = 0; i < size; i++) {
- CStructAccess.WriteLongNode.writeLong(pointer, i * Long.BYTES, referencesToBeFreed.get(i));
+ NativeMemory.writeLongArrayElement(pointer, i, referencesToBeFreed.get(i));
}
- PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_BULK_DEALLOC, pointer, size);
- FreeNode.freeLong(pointer);
+ try {
+ ExternalFunctionInvoker.invokeBULK_DEALLOC(
+ CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_BULK_DEALLOC).getAddress(), pointer, size);
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
+ free(pointer);
referencesToBeFreed.clear();
} finally {
CExtCommonNodes.ReadAndClearNativeException.executeUncached(threadState);
@@ -655,53 +704,55 @@ private static void releaseNativeObjects(PythonContext context, ArrayList
}
}
- @TruffleBoundary
- public static void releaseNativeWrapperUncached(PythonNativeWrapper nativeWrapper) {
- releaseNativeWrapper(nativeWrapper, FreeNode.getUncached());
- }
-
/**
* Releases a native wrapper. This requires to remove the native wrapper from any lookup tables
* and to free potentially allocated native resources. If native wrappers receive
* {@code toNative}, either a handle pointer is allocated or some off-heap memory is
* allocated. This method takes care of that and will also free any off-heap memory.
*/
- public static void releaseNativeWrapper(PythonNativeWrapper nativeWrapper, FreeNode freeNode) {
+ @TruffleBoundary
+ public static void releaseNativeWrapper(long nativePointer) {
// If wrapper already received toNative, release the handle or free the native memory.
- if (nativeWrapper.isNative()) {
- long nativePointer = nativeWrapper.getNativePointer();
+ if (nativePointer != PythonObject.UNINITIALIZED) {
if (LOGGER.isLoggable(Level.FINE)) {
- LOGGER.fine(PythonUtils.formatJString("Freeing pointer: 0x%x (wrapper: %s ;; object: %s)", nativePointer, nativeWrapper, nativeWrapper.getDelegate()));
+ LOGGER.fine(PythonUtils.formatJString("Freeing native replacement/stub object with pointer: 0x%x", nativePointer));
}
+ PythonContext pythonContext = PythonContext.get(null);
if (HandlePointerConverter.pointsToPyHandleSpace(nativePointer)) {
- if (HandlePointerConverter.pointsToPyIntHandle(nativePointer)) {
- return;
- } else if (HandlePointerConverter.pointsToPyFloatHandle(nativePointer)) {
+ if (HandlePointerConverter.pointsToPyIntHandle(nativePointer) || HandlePointerConverter.pointsToPyFloatHandle(nativePointer)) {
return;
}
// In this case, we are up to free a native object stub.
- assert tableEntryRemoved(PythonContext.get(freeNode).nativeContext, nativeWrapper);
+ assert tableEntryRemoved(pythonContext.handleContext, nativePointer);
nativePointer = HandlePointerConverter.pointerToStub(nativePointer);
} else {
- nativeLookupRemove(PythonContext.get(freeNode).nativeContext, nativePointer);
+ nativeLookupRemove(pythonContext.handleContext, nativePointer);
}
- freeNode.free(nativePointer);
+ free(nativePointer);
}
}
- private static boolean tableEntryRemoved(HandleContext context, PythonNativeWrapper nativeWrapper) {
- PythonObjectReference ref = nativeWrapper.ref;
- if (ref != null) {
- int id = ref.getHandleTableIndex();
- return id <= 0 || nativeStubLookupGet(context, nativeWrapper.getNativePointer(), id) == null;
- }
- // there cannot be a table entry if the wrapper does not have a PythonObjectReference
- return true;
+ private static boolean tableEntryRemoved(HandleContext context, long pointer) {
+ assert HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ int id = readIntField(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
+ return id <= 0 || nativeStubLookupGet(context, pointer, id) == null;
}
+ /**
+ * Iterates through the native lookup table and for all Python object references that were not
+ * allocated from Java, it decrefs {@link PythonObject#MANAGED_REFCNT}, and if the refcount is
+ * then {@code 0} it eventually calls {@code Py_Dealloc} on those objects. Hence, this method
+ * may run user code.
+ *
+ * @param context
+ * @param handleContext
+ */
public static void deallocNativeReplacements(PythonContext context, HandleContext handleContext) {
assert context.ownsGil();
+ assert context.isFinalizing();
+ assert !context.getEnv().getContext().isCancelling() : "must not run user code when canceling";
+
ArrayList referencesToBeFreed = new ArrayList<>();
Iterator>> iterator = handleContext.nativeLookup.entrySet().iterator();
while (iterator.hasNext()) {
@@ -728,17 +779,20 @@ public static void freeNativeReplacementStructs(PythonContext context, HandleCon
assert context.ownsGil();
handleContext.nativeLookup.forEach((l, ref) -> {
if (ref instanceof PythonObjectReference reference) {
- // We don't expect references to wrappers that would have a native object stub.
+ // We don't expect references to objects that would have a native object stub.
assert reference.handleTableIndex == -1;
- // We expect at this point that most if not all references left were allocated
- // from Java and can be freed here. There may be stragglers that are waiting
- // for a GC though, so we have to check.
+ /*
+ * We expect at this point that most if not all references left were allocated from
+ * Java and can be freed here. There may be stragglers that are waiting for a GC
+ * though, so we have to check.
+ */
if (reference.isAllocatedFromJava()) {
freeNativeStruct(reference);
}
}
});
handleContext.nativeLookup.clear();
+ Arrays.fill(handleContext.nativeTypeLookup, null);
}
public static boolean disableReferenceQueuePolling(HandleContext handleContext) {
@@ -765,29 +819,37 @@ public static void disableReferenceQueuePollingPermanently(HandleContext handleC
}
private static void freeNativeStub(PythonObjectReference ref) {
- assert HandlePointerConverter.pointsToPyHandleSpace(ref.pointer);
- assert !HandlePointerConverter.pointsToPyIntHandle(ref.pointer);
- assert !HandlePointerConverter.pointsToPyFloatHandle(ref.pointer);
- if (ref.gc) {
- PyObjectGCDelNode.executeUncached(ref.pointer);
+ freeNativeStub(ref.pointer, ref.gc);
+ }
+
+ private static void freeNativeStub(long pointer, boolean gc) {
+ assert HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ assert !HandlePointerConverter.pointsToPyIntHandle(pointer);
+ assert !HandlePointerConverter.pointsToPyFloatHandle(pointer);
+ if (gc) {
+ PyObjectGCDelNode.executeUncached(pointer);
} else {
- long rawPointer = HandlePointerConverter.pointerToStub(ref.pointer);
+ long rawPointer = HandlePointerConverter.pointerToStub(pointer);
LOGGER.fine(() -> PythonUtils.formatJString("releasing native object stub 0x%x", rawPointer));
- FreeNode.executeUncached(rawPointer);
+ free(rawPointer);
}
}
+ /**
+ * Free a native structure that was allocated from Java and is tied to the lifetime of a Java
+ * object. This method will be called during context finalization and must not run user code.
+ */
private static void freeNativeStruct(PythonObjectReference ref) {
assert ref.handleTableIndex == -1;
assert ref.isAllocatedFromJava();
assert !ref.gc;
- LOGGER.fine(() -> PythonUtils.formatJString("releasing %s", ref.toString()));
- FreeNode.executeUncached(ref.pointer);
+ LOGGER.fine(() -> PythonUtils.formatJString("releasing %s", ref));
+ free(ref.pointer);
}
private static void freeNativeStorage(NativeStorageReference ref) {
- LOGGER.fine(() -> PythonUtils.formatJString("releasing %s", ref.toString()));
- FreeNode.executeUncached(ref.ptr);
+ LOGGER.fine(() -> PythonUtils.formatJString("releasing %s", ref));
+ free(ref.ptr);
}
/**
@@ -799,11 +861,28 @@ public static void freeNativeObjectStubs(HandleContext handleContext) {
// TODO(fa): this should not require the GIL (GR-51314)
assert PythonContext.get(null).ownsGil();
assert PythonContext.get(null).isFinalizing();
- for (PythonObjectReference ref : handleContext.nativeStubLookup) {
- if (ref != null) {
- nativeStubLookupRemove(handleContext, ref);
- freeNativeStub(ref);
+ for (int i = HandleContext.FIRST_VALID_INDEX; i < handleContext.nativeStubLookup.length; i++) {
+ Object ref = handleContext.nativeStubLookup[i];
+ // not all slots of the handle table are currently used
+ if (ref == null) {
+ continue;
}
+
+ assert ref instanceof PythonObject || ref instanceof PythonObjectReference || CApiContext.isSpecialSingleton(ref);
+
+ nativeStubLookupRemove(handleContext, i);
+ if (ref instanceof PythonObjectReference pythonObjectReference) {
+ freeNativeStub(pythonObjectReference);
+ } else if (ref instanceof PythonObject pythonObject) {
+ long pointer = pythonObject.getNativePointer();
+ pythonObject.clearNativePointer();
+ Object type = GetClassNode.executeUncached(pythonObject);
+ boolean isGc = (GetTypeFlagsNode.executeUncached(type) & TypeFlags.HAVE_GC) != 0;
+ // all pointers in 'nativeStubLookup' need to be tagged pointers
+ assert HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ freeNativeStub(pointer, isGc);
+ }
+ // The remaining type of objects are special singletons which are free'd separately.
}
}
@@ -826,7 +905,7 @@ public static void freeNativeStorages(HandleContext handleContext) {
*/
@TruffleBoundary
public static void addNativeWeakRef(PythonContext pythonContext, PythonAbstractNativeObject object) {
- pythonContext.nativeContext.nativeWeakRef.put(getNativePointer(object), 0L);
+ pythonContext.handleContext.nativeWeakRef.put(getNativePointer(object), 0L);
}
/**
@@ -835,7 +914,7 @@ public static void addNativeWeakRef(PythonContext pythonContext, PythonAbstractN
*/
@TruffleBoundary
public static void removeNativeWeakRef(PythonContext pythonContext, long pointer) {
- pythonContext.nativeContext.nativeWeakRef.remove(pointer);
+ pythonContext.handleContext.nativeWeakRef.remove(pointer);
}
public static long getNativePointer(Object obj) {
@@ -854,7 +933,7 @@ public static long getNativePointer(Object obj) {
public static void deallocateNativeWeakRefs(PythonContext pythonContext) {
CompilerAsserts.neverPartOfCompilation();
assert pythonContext.ownsGil();
- HandleContext context = pythonContext.nativeContext;
+ HandleContext context = pythonContext.handleContext;
int idx = -1;
Object[] list = context.nativeWeakRef.values().toArray();
context.nativeWeakRef.clear();
@@ -866,12 +945,17 @@ public static void deallocateNativeWeakRefs(PythonContext pythonContext) {
}
if (idx != -1) {
int len = idx + 1;
- Object array = CStructAccess.AllocateNode.allocUncached((long) len * Long.BYTES);
+ long array = mallocPtrArray(len);
try {
- CStructAccess.WritePointerNode.getUncached().writePointerArray(array, ptrArray, len, 0, 0);
- CExtNodes.PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_SHUTDOWN_BULK_DEALLOC, array, len);
+ writePtrArrayElements(array, 0, ptrArray, 0, len);
+ try {
+ ExternalFunctionInvoker.invokeSHUTDOWN_BULK_DEALLOC(
+ CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_SHUTDOWN_BULK_DEALLOC).getAddress(), array, len);
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
} finally {
- CStructAccess.FreeNode.executeUncached(array);
+ free(array);
context.nativeWeakRef.clear();
}
}
@@ -914,13 +998,17 @@ public static IdReference> nativeLookupGet(HandleContext context, long pointer
}
@TruffleBoundary
- public static IdReference> nativeLookupPut(HandleContext context, long pointer, NativeObjectReference value) {
- return context.nativeLookup.put(pointer, value);
+ public static void nativeLookupPut(HandleContext context, long pointer, NativeObjectReference value) {
+ assert !HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ assert !context.nativeLookup.containsKey(pointer) || context.nativeLookup.get(pointer).get() == null;
+ context.nativeLookup.put(pointer, value);
}
@TruffleBoundary
- public static IdReference> nativeLookupPut(HandleContext context, long pointer, PythonObjectReference value) {
- return context.nativeLookup.put(pointer, value);
+ public static void nativeLookupPut(HandleContext context, long pointer, PythonObjectReference value) {
+ assert !HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ assert !context.nativeLookup.containsKey(pointer) || context.nativeLookup.get(pointer).get() == null;
+ context.nativeLookup.put(pointer, value);
}
@TruffleBoundary
@@ -928,14 +1016,70 @@ public static IdReference> nativeLookupRemove(HandleContext context, long poin
return context.nativeLookup.remove(pointer);
}
- public static PythonObjectReference nativeStubLookupGet(HandleContext context, long pointer, int idx) {
+ public static IdReference> nativeTypeLookupGet(HandleContext context, long pointer, int idx) {
+ assert idx != 0;
+ assert !HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ IdReference> result = context.nativeTypeLookup[idx];
+ assert result == nativeLookupGet(context, pointer);
+ return result;
+ }
+
+ @TruffleBoundary
+ private static int nativeTypeLookupPut(HandleContext context, IdReference> value, long pointer) throws OverflowException {
+ assert !HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ for (int i = 0; i < context.nativeTypeLookup.length; i++) {
+ if (context.nativeTypeLookup[i] == null) {
+ context.nativeTypeLookup[i] = value;
+ return i;
+ }
+ }
+ // table is full; resize it
+ int oldSize = context.nativeTypeLookup.length;
+ /*
+ * Creating types is an expensive operation and types are usually created once and stay
+ * alive for a long time (if not immortal). We therefore always grow linearly.
+ */
+ int newSize = PythonUtils.addExact(oldSize, 64);
+ assert newSize != oldSize;
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(String.format("Resizing native type lookup table: %d -> %d", oldSize, newSize));
+ }
+ context.nativeTypeLookup = Arrays.copyOf(context.nativeTypeLookup, newSize);
+ assert context.nativeTypeLookup[oldSize] == null;
+ context.nativeTypeLookup[oldSize] = value;
+ return oldSize;
+ }
+
+ @TruffleBoundary
+ private static void nativeTypeLookupUpdate(HandleContext context, int typeLookupIdx, IdReference> value) {
+ assert 0 < typeLookupIdx && typeLookupIdx < context.nativeTypeLookup.length;
+ assert context.nativeTypeLookup[typeLookupIdx] != null;
+ assert context.nativeTypeLookup[typeLookupIdx].get() == null;
+ context.nativeTypeLookup[typeLookupIdx] = value;
+ }
+
+ public static void nativeTypeLookupRemove(HandleContext context, long ptr, int typeLookupIdx) {
+ CompilerAsserts.neverPartOfCompilation();
+ assert 0 < typeLookupIdx && typeLookupIdx < context.nativeTypeLookup.length;
+ assert context.nativeTypeLookup[typeLookupIdx] != null;
+ assert isValidTypeLookupEntry(context.nativeTypeLookup[typeLookupIdx].get(), ptr);
+ context.nativeTypeLookup[typeLookupIdx] = null;
+ }
+
+ private static boolean isValidTypeLookupEntry(Object entry, long ptr) {
+ return entry == null ||
+ entry instanceof PythonManagedClass managedClass && managedClass.getNativePointer() == ptr ||
+ entry instanceof PythonNativeClass nativeClass && nativeClass.getPtr() == ptr;
+ }
+
+ public static Object nativeStubLookupGet(HandleContext context, long pointer, int idx) {
if (idx <= 0) {
if (PythonContext.DEBUG_CAPI && HandleContext.getShadowTable(context.nativeStubLookupShadowTable, pointer) != null) {
throw CompilerDirectives.shouldNotReachHere();
}
return null;
}
- PythonObjectReference result = context.nativeStubLookup[idx];
+ Object result = context.nativeStubLookup[idx];
if (PythonContext.DEBUG_CAPI && HandleContext.getShadowTable(context.nativeStubLookupShadowTable, pointer) != result) {
throw CompilerDirectives.shouldNotReachHere();
}
@@ -944,8 +1088,7 @@ public static PythonObjectReference nativeStubLookupGet(HandleContext context, l
/**
* Reserves a free slot in the handle table that can later be used to store a
- * {@link PythonObjectReference} using
- * {@link #nativeStubLookupPut(HandleContext, PythonObjectReference)}. If the handle table is
+ * {@link PythonObjectReference} using {@link #nativeStubLookupPut}. If the handle table is
* currently too small, it will be enlarged.
*
* @throws OverflowException Indicates that we cannot resize the handle table anymore. This
@@ -974,13 +1117,14 @@ private static int resizeNativeStubLookupTable(HandleContext context) throws Ove
return context.nativeStubLookupFreeStack.pop();
}
- private static int nativeStubLookupPut(HandleContext context, PythonObjectReference value) {
- assert value.handleTableIndex > 0;
- final int idx = value.handleTableIndex;
+ private static int nativeStubLookupPut(HandleContext context, int idx, Object value, long pointer) {
+ assert idx > 0;
+ assert HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ assert value instanceof PythonObject || value instanceof PythonObjectReference || CApiContext.isSpecialSingleton(value);
assert context.nativeStubLookup[idx] == null || context.nativeStubLookup[idx] == value;
context.nativeStubLookup[idx] = value;
if (PythonContext.DEBUG_CAPI) {
- PythonObjectReference prev = HandleContext.putShadowTable(context.nativeStubLookupShadowTable, value.pointer, value);
+ Object prev = HandleContext.putShadowTable(context.nativeStubLookupShadowTable, pointer, value);
if (prev != null && prev != value) {
throw CompilerDirectives.shouldNotReachHere();
}
@@ -988,13 +1132,33 @@ private static int nativeStubLookupPut(HandleContext context, PythonObjectRefere
return idx;
}
- public static PythonObjectReference nativeStubLookupRemove(HandleContext context, PythonObjectReference ref) {
- assert ref.handleTableIndex > 0;
- final int idx = ref.handleTableIndex;
- PythonObjectReference result = context.nativeStubLookup[idx];
+ private static int nativeStubLookupReplaceByWeak(HandleContext context, int idx, PythonObjectReference value, long pointer) {
+ assert idx > 0;
+ assert idx == value.handleTableIndex;
+ assert HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ assert context.nativeStubLookup[idx] == value.get();
+ context.nativeStubLookup[idx] = value;
+ if (PythonContext.DEBUG_CAPI) {
+ Object prev = HandleContext.putShadowTable(context.nativeStubLookupShadowTable, pointer, value);
+ if (prev != value.get()) {
+ throw CompilerDirectives.shouldNotReachHere();
+ }
+ }
+ return idx;
+ }
+
+ public static void nativeStubLookupRemove(HandleContext context, PythonObjectReference ref) {
+ assert HandlePointerConverter.pointsToPyHandleSpace(ref.pointer);
+ nativeStubLookupRemove(context, ref.handleTableIndex);
+ }
+
+ public static Object nativeStubLookupRemove(HandleContext context, int idx) {
+ assert idx >= HandleContext.FIRST_VALID_INDEX;
+ Object result = context.nativeStubLookup[idx];
+ assert result instanceof PythonObjectReference || result instanceof PythonObject || CApiContext.isSpecialSingleton(result);
context.nativeStubLookup[idx] = null;
context.nativeStubLookupFreeStack.push(idx);
- if (PythonContext.DEBUG_CAPI && HandleContext.removeShadowTable(context.nativeStubLookupShadowTable, ref.pointer) != result) {
+ if (PythonContext.DEBUG_CAPI && HandleContext.removeShadowTable(context.nativeStubLookupShadowTable, result) != result) {
throw CompilerDirectives.shouldNotReachHere();
}
return result;
@@ -1060,112 +1224,128 @@ public static double pointerToDouble(long pointer) {
@GenerateUncached
@GenerateInline
@GenerateCached(false)
- @ImportStatic(CApiGuards.class)
+ @ImportStatic({CApiContext.class, PGuards.class})
public abstract static class FirstToNativeNode extends Node {
- public static long executeUncached(PythonAbstractObjectNativeWrapper wrapper, boolean immortal) {
- return FirstToNativeNodeGen.getUncached().execute(null, wrapper, immortal);
+ public static long executeUncached(PythonAbstractObject object, long initialRefCount) {
+ return FirstToNativeNodeGen.getUncached().execute(null, object, initialRefCount);
}
- public final long execute(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper) {
- return execute(inliningTarget, wrapper, false);
- }
-
- public abstract long execute(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper, boolean immortal);
+ public abstract long execute(Node inliningTarget, PythonAbstractObject object, long initialRefCount);
@Specialization
- static long doPrimitiveNativeWrapper(Node inliningTarget, PrimitiveNativeWrapper wrapper, boolean immortal,
- @Shared @Cached(inline = false) CStructAccess.WriteDoubleNode writeDoubleNode,
- @Exclusive @Cached InlinedConditionProfile isFloatObjectProfile,
- @Exclusive @Cached AllocateNativeObjectStubNode allocateNativeObjectStubNode) {
- boolean isFloat = isFloatObjectProfile.profile(inliningTarget, wrapper.isDouble());
- CStructs ctype = isFloat ? CStructs.GraalPyFloatObject : CStructs.GraalPyObject;
- Object type;
- if (wrapper.isBool()) {
- type = PythonBuiltinClassType.Boolean;
- } else if (wrapper.isInt()) {
- return HandlePointerConverter.intToPointer(wrapper.getInt());
- } else if (wrapper.isLong()) {
- long value = wrapper.getLong();
- if (PInt.fitsInInt(value)) {
- return HandlePointerConverter.intToPointer((int) value);
- }
- type = PythonBuiltinClassType.PInt;
- } else if (isFloat) {
- double d = wrapper.getDouble();
- if (PFloat.fitsInFloat(d)) {
- return HandlePointerConverter.floatToPointer((float) d);
+ @TruffleBoundary
+ static long doPythonManagedClass(@SuppressWarnings("unused") Node inliningTarget, PythonManagedClass clazz, @SuppressWarnings("unused") long initialRefCount) {
+ assert !clazz.isNative();
+ /*
+ * Note: it's important that we first allocate the empty 'PyTypeStruct' and register it
+ * to the wrapper before we do the type's initialization. Otherwise, we will run into an
+ * infinite recursion because, e.g., some type uses 'None', so the 'NoneType' will be
+ * transformed to native but 'NoneType' may have some field that is initialized with
+ * 'None' and so on.
+ *
+ * If we first set the empty struct and initialize it afterward, everything is fine.
+ */
+ boolean heaptype = (GetTypeFlagsNode.executeUncached(clazz) & TypeFlags.HEAPTYPE) != 0;
+ long size = CStructs.PyTypeObject.size();
+ if (heaptype) {
+ size = CStructs.PyHeapTypeObject.size();
+ if (GetClassNode.executeUncached(clazz) instanceof PythonAbstractNativeObject nativeMetatype) {
+ // TODO should call the metatype's tp_alloc
+ size = TypeNodes.GetBasicSizeNode.executeUncached(nativeMetatype);
}
- type = PythonBuiltinClassType.PFloat;
- } else {
- throw CompilerDirectives.shouldNotReachHere();
}
- long taggedPointer = allocateNativeObjectStubNode.execute(inliningTarget, wrapper, type, ctype, immortal, false);
+ /*
+ * For built-in classes, we can always create a strong reference. Those classes are
+ * always reachable and a weak reference is not necessary. We will release the native
+ * memory in the C API finalization.
+ */
+ boolean isBuiltinClass = clazz instanceof PythonBuiltinClass;
- // allocate a native stub object (C type: GraalPy*Object)
- if (isFloat) {
- long realPointer = HandlePointerConverter.pointerToStub(taggedPointer);
- writeDoubleNode.write(realPointer, CFields.GraalPyFloatObject__ob_fval, wrapper.getDouble());
- }
- return taggedPointer;
+ long ptr = NativeMemory.malloc(size);
+ int typeReference = CApiTransitions.createPythonManagedClassReference(clazz, ptr, true);
+ ToNativeTypeNode.initializeType(clazz, ptr, heaptype, typeReference);
+ assert !isBuiltinClass || clazz.getRefCount() == IMMORTAL_REFCNT;
+ return ptr;
+ }
+
+ @Specialization
+ @TruffleBoundary
+ static long doMemoryView(@SuppressWarnings("unused") Node inliningTarget, PMemoryView mv, long initialRefCount) {
+ assert !mv.isNative();
+ assert initialRefCount == IMMORTAL_REFCNT;
+ long ptr = PyMemoryViewWrapper.allocate(mv);
+ CApiTransitions.createReference(mv, ptr);
+ return ptr;
+ }
+
+ @Specialization(guards = "isSpecialSingleton(singletonObject)")
+ @TruffleBoundary
+ static long doSpecialSingleton(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractObject singletonObject, long initialRefCount) {
+ assert initialRefCount == IMMORTAL_REFCNT;
+ assert !PythonToNativeInternalNode.mapsToNull(singletonObject);
+
+ Object type = GetClassNode.executeUncached(singletonObject);
+ assert (GetTypeFlagsNode.executeUncached(type) & TypeFlags.HAVE_GC) == 0;
+
+ return AllocateNativeObjectStubNodeGen.getUncached().execute(inliningTarget, singletonObject, type, CStructs.GraalPyObject, IMMORTAL_REFCNT, false);
}
- @Specialization(guards = "!isPrimitiveNativeWrapper(wrapper)")
- static long doOther(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper, boolean immortal,
- @Cached(inline = false) CStructAccess.WriteLongNode writeLongNode,
- @Cached(inline = false) CStructAccess.WritePointerNode writePointerNode,
- @Shared @Cached(inline = false) CStructAccess.WriteDoubleNode writeDoubleNode,
+ @Specialization(guards = {"!isManagedClass(pythonObject)", "!isMemoryView(pythonObject)"})
+ static long doOther(Node inliningTarget, PythonObject pythonObject, long initialRefCount,
@Exclusive @Cached InlinedConditionProfile isVarObjectProfile,
@Exclusive @Cached InlinedConditionProfile isGcProfile,
@Exclusive @Cached InlinedConditionProfile isFloatObjectProfile,
- @Exclusive @Cached InlinedConditionProfile isMemViewObjectProfile,
- @Cached GetClassNode getClassNode,
+ @Cached GetPythonObjectClassNode getClassNode,
@Cached(inline = false) GetTypeFlagsNode getTypeFlagsNode,
@Exclusive @Cached AllocateNativeObjectStubNode allocateNativeObjectStubNode) {
- assert !(wrapper instanceof TruffleObjectNativeWrapper);
- assert !(wrapper instanceof PrimitiveNativeWrapper);
+ // for types, we always need to allocate the full PyTypeObject
+ assert !(pythonObject instanceof PythonManagedClass);
+ // for memoryview, we always need to allocate the full PyMemoryViewObject
+ assert !(pythonObject instanceof PMemoryView);
+ assert !CApiContext.isSpecialSingleton(pythonObject);
- Object delegate = wrapper.getDelegate();
- Object type = getClassNode.execute(inliningTarget, delegate);
+ Object type = getClassNode.execute(inliningTarget, pythonObject);
CStructs ctype;
- if (isVarObjectProfile.profile(inliningTarget, delegate instanceof PTuple)) {
+ if (isVarObjectProfile.profile(inliningTarget, pythonObject instanceof PTuple)) {
ctype = CStructs.GraalPyVarObject;
- } else if (isFloatObjectProfile.profile(inliningTarget, delegate instanceof PFloat)) {
+ } else if (isFloatObjectProfile.profile(inliningTarget, pythonObject instanceof PFloat)) {
ctype = CStructs.GraalPyFloatObject;
} else {
ctype = CStructs.GraalPyObject;
}
boolean gc = isGcProfile.profile(inliningTarget, (getTypeFlagsNode.execute(type) & TypeFlags.HAVE_GC) != 0);
- long taggedPointer = allocateNativeObjectStubNode.execute(inliningTarget, wrapper, type, ctype, immortal, gc);
+ long taggedPointer = allocateNativeObjectStubNode.execute(inliningTarget, pythonObject, type, ctype, initialRefCount, gc);
// allocate a native stub object (C type: GraalPy*Object)
if (ctype == CStructs.GraalPyVarObject) {
- assert delegate instanceof PTuple;
- SequenceStorage sequenceStorage = ((PTuple) delegate).getSequenceStorage();
+ assert pythonObject instanceof PTuple;
+ SequenceStorage sequenceStorage = ((PTuple) pythonObject).getSequenceStorage();
long realPointer = HandlePointerConverter.pointerToStub(taggedPointer);
- writeLongNode.write(realPointer, CFields.GraalPyVarObject__ob_size, sequenceStorage.length());
- Object obItemPtr = 0L;
+ writeLongField(realPointer, CFields.GraalPyVarObject__ob_size, sequenceStorage.length());
+ long obItemPtr = NULLPTR;
if (sequenceStorage instanceof NativeSequenceStorage nativeSequenceStorage) {
obItemPtr = nativeSequenceStorage.getPtr();
}
- writePointerNode.write(realPointer, CFields.GraalPyVarObject__ob_item, obItemPtr);
+ writePtrField(realPointer, CFields.GraalPyVarObject__ob_item, obItemPtr);
} else if (ctype == CStructs.GraalPyFloatObject) {
- assert delegate instanceof Double || delegate instanceof PFloat;
+ assert pythonObject instanceof PFloat;
long realPointer = HandlePointerConverter.pointerToStub(taggedPointer);
- double fval;
- if (delegate instanceof Double d) {
- fval = d;
- } else {
- fval = ((PFloat) delegate).getValue();
- }
- writeDoubleNode.write(realPointer, CFields.GraalPyFloatObject__ob_fval, fval);
+ writeDoubleField(realPointer, CFields.GraalPyFloatObject__ob_fval, ((PFloat) pythonObject).getValue());
}
return taggedPointer;
}
+
+ public static long getInitialRefcnt(boolean newRef, boolean immortal) {
+ if (immortal) {
+ return IMMORTAL_REFCNT;
+ }
+ return MANAGED_REFCNT + (newRef ? 1 : 0);
+ }
}
@GenerateUncached
@@ -1173,47 +1353,40 @@ static long doOther(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapp
@GenerateCached(false)
abstract static class AllocateNativeObjectStubNode extends Node {
- abstract long execute(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper, Object type, CStructs ctype, boolean immortal, boolean gc);
+ abstract long execute(Node inliningTarget, PythonAbstractObject object, Object type, CStructs ctype, long initialRefCount, boolean gc);
@Specialization
- static long doGeneric(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper, Object type, CStructs ctype, boolean immortal, boolean gc,
+ static long doGeneric(Node inliningTarget, PythonAbstractObject object, Object type, CStructs ctype, long initialRefCount, boolean gc,
@Cached(inline = false) GilNode gil,
- @Cached(inline = false) CStructAccess.AllocateNode allocateNode,
- @Cached(inline = false) CStructAccess.WriteLongNode writeLongNode,
@Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObjectNode,
- @Cached(inline = false) CStructAccess.ReadI32Node readI32Node,
- @Cached(inline = false) CStructAccess.WriteIntNode writeIntNode,
- @Cached(inline = false) CStructAccess.GetElementPtrNode getElementPtrNode,
- @Cached CoerceNativePointerToLongNode coerceToLongNode) {
+ @Cached PyObjectGCTrackNode gcTrackNode) {
- log(wrapper);
+ log(object);
pollReferenceQueue();
- long initialRefCount = immortal ? IMMORTAL_REFCNT : MANAGED_REFCNT;
-
/*
* Allocate a native stub object (C type: GraalPy*Object). For types that participate in
* Python's GC, we will also allocate space for 'PyGC_Head'.
*/
long presize = gc ? CStructs.PyGC_Head.size() : 0;
- Object nativeObjectStub = allocateNode.alloc(ctype.size() + presize);
+ long stubPointer = calloc(ctype.size() + presize);
PythonContext pythonContext = PythonContext.get(inliningTarget);
- HandleContext handleContext = pythonContext.nativeContext;
- long stubPointer = coerceToLongNode.execute(inliningTarget, nativeObjectStub);
+ HandleContext handleContext = pythonContext.handleContext;
long taggedPointer = HandlePointerConverter.stubToPointer(stubPointer);
+ long taggedGCHead = 0;
if (gc) {
// adjust allocation count of generation
// GCState *gcstate = get_gc_state();
- Object gcState = pythonContext.getCApiContext().getGCState();
- assert gcState != null;
+ long gcState = pythonContext.getCApiContext().getGCState();
+ assert gcState != 0L;
// compute start address of embedded array; essentially '&gcstate->generations[0]'
- Object generations = getElementPtrNode.getElementPtr(gcState, CFields.GCState__generations);
+ long generations = CStructAccess.getFieldPtr(gcState, CFields.GCState__generations);
// gcstate->generations[0].count++;
- int count = readI32Node.read(generations, CFields.GCGeneration__count);
- writeIntNode.write(generations, CFields.GCGeneration__count, count + 1);
+ int count = CStructAccess.readIntField(generations, CFields.GCGeneration__count);
+ CStructAccess.writeIntField(generations, CFields.GCGeneration__count, count + 1);
/*
* The corresponding location in CPython (i.e. 'typeobject.c: PyType_GenericAlloc')
@@ -1223,11 +1396,12 @@ static long doGeneric(Node inliningTarget, PythonAbstractObjectNativeWrapper wra
*/
// same as in 'gcmodule.c: gc_alloc': PyObject *op = (PyObject *)(mem + presize);
+ taggedGCHead = taggedPointer;
stubPointer += presize;
taggedPointer += presize;
}
- writeLongNode.write(stubPointer, CFields.PyObject__ob_refcnt, initialRefCount);
+ CStructAccess.writeLongField(stubPointer, CFields.PyObject__ob_refcnt, initialRefCount);
// TODO(fa): this should not require the GIL (GR-51314)
boolean acquired = gil.acquire();
@@ -1237,9 +1411,29 @@ static long doGeneric(Node inliningTarget, PythonAbstractObjectNativeWrapper wra
// We don't allow 'handleTableIndex == 0' to avoid that zeroed memory
// accidentally maps to some valid object.
assert idx > 0;
- writeIntNode.write(stubPointer, CFields.GraalPyObject__handle_table_index, idx);
- PythonObjectReference ref = PythonObjectReference.createStub(handleContext, wrapper, immortal, taggedPointer, idx, gc);
- nativeStubLookupPut(handleContext, ref);
+ CStructAccess.writeIntField(stubPointer, CFields.GraalPyObject__handle_table_index, idx);
+ Object ref;
+ if (initialRefCount > MANAGED_REFCNT) {
+ ref = object;
+
+ assert !gc || taggedGCHead != 0;
+ if (gc) {
+ /*
+ * Note: The following part will require the GIL even if we resolve GR-51314
+ * and remove the outer acquire.
+ */
+ assert pythonContext.ownsGil();
+ gcTrackNode.executeGc(inliningTarget, taggedGCHead);
+ }
+ } else {
+ /*
+ * If the object is not a 'PythonObject', it is expected to be immortal and will
+ * not reach this branch. This is, e.g., the case for singletons like PNone.
+ */
+ assert !CApiContext.isSpecialSingleton(object);
+ ref = PythonObjectReference.createStub(handleContext, (PythonObject) object, false, taggedPointer, idx, gc);
+ }
+ nativeStubLookupPut(handleContext, idx, ref, taggedPointer);
} catch (OverflowException e) {
/*
* The OverflowException may be thrown by 'nativeStubLookupReserve' and indicates
@@ -1261,7 +1455,7 @@ static long doGeneric(Node inliningTarget, PythonAbstractObjectNativeWrapper wra
*/
@TruffleBoundary
@SuppressWarnings("try")
- public static void createReference(PythonNativeWrapper obj, long ptr, boolean allocatedFromJava) {
+ public static void createReference(PythonObject obj, long ptr) {
try (GilNode.UncachedAcquire ignored = GilNode.uncachedAcquire()) {
/*
* The first test if '!obj.isNative()' in the caller is done on a fast-path but not
@@ -1272,11 +1466,59 @@ public static void createReference(PythonNativeWrapper obj, long ptr, boolean al
obj.setNativePointer(ptr);
pollReferenceQueue();
HandleContext context = getContext();
- nativeLookupPut(context, ptr, PythonObjectReference.createReplacement(context, obj, ptr, allocatedFromJava));
+ nativeLookupPut(context, ptr, PythonObjectReference.createReplacement(context, obj, ptr, false));
}
}
}
+ /**
+ * Creates a {@link PythonObjectReference} to the given managed class and connects that to the
+ * given native {@code pointer} such that the {@code pointer} can be resolved to the
+ * {@code delegate}. This should be used for types because it will set up a fast path for
+ * resolving the type's pointer. This fast path can be used if it is known that the pointer is a
+ * {@code PyTypeObject *}.
+ */
+ public static int createPythonManagedClassReference(PythonManagedClass clazz, long ptr, boolean allocatedFromJava) {
+ CompilerAsserts.neverPartOfCompilation();
+ assert PythonContext.get(null).ownsGil();
+ assert !clazz.isNative();
+
+ logVoid(clazz, ptr);
+ clazz.setNativePointer(ptr);
+ HandleContext context = getContext();
+ PythonObjectReference pythonObjectReference = PythonObjectReference.createReplacement(context, clazz, ptr, allocatedFromJava);
+ nativeLookupPut(context, ptr, pythonObjectReference);
+ try {
+ return nativeTypeLookupPut(context, pythonObjectReference, ptr);
+ } catch (OverflowException e) {
+ // ignore; it's an optimization
+ return 0;
+ }
+ }
+
+ /**
+ * Creates a {@link PythonObjectReference} to the given managed class and connects that to the
+ * given native {@code pointer} such that the {@code pointer} can be resolved to the
+ * {@code delegate}. This should be used for types because it will set up a fast path for
+ * resolving the type's pointer. This fast path can be used if it is known that the pointer is a
+ * {@code PyTypeObject *}.
+ */
+ public static int createPythonNativeClassReference(PythonNativeClass clazz) {
+ CompilerAsserts.neverPartOfCompilation();
+ assert PythonContext.get(null).ownsGil();
+
+ logVoid("Creating fast type lookup for native class ", clazz);
+ HandleContext context = getContext();
+
+ try {
+ PythonAbstractNativeObject nativeObject = (PythonAbstractNativeObject) clazz;
+ return nativeTypeLookupPut(context, nativeObject.ref, clazz.getPtr());
+ } catch (OverflowException e) {
+ // ignore; it's an optimization
+ return 0;
+ }
+ }
+
/**
* Creates a weak reference to {@code delegate} and connects that to the given {@code pointer}
* object such that the {@code pointer} can be resolved to the {@code delegate}.
@@ -1352,83 +1594,24 @@ private static T logResult(T value) {
return value;
}
- /**
- * Resolves a native handle to the corresponding {@link PythonNativeWrapper}. This node assumes
- * that {@code pointer} points to handle space (i.e.
- * {@link HandlePointerConverter#pointsToPyHandleSpace(long)} is {@code true}) and essential
- * just looks up the handle in the table. It will additionally increment the reference count if
- * the wrapper is a subclass of {@link PythonAbstractObjectNativeWrapper}.
- */
- @GenerateUncached
- @GenerateInline
- @GenerateCached(false)
- public abstract static class ResolveHandleNode extends Node {
-
- public abstract PythonNativeWrapper execute(Node inliningTarget, long pointer);
-
- @Specialization
- static PythonNativeWrapper doGeneric(Node inliningTarget, long pointer,
- @Cached(inline = false) CStructAccess.ReadI32Node readI32Node,
- @Cached InlinedExactClassProfile profile,
- @Cached UpdateStrongRefNode updateRefNode) {
- if (HandlePointerConverter.pointsToPyIntHandle(pointer)) {
- throw CompilerDirectives.shouldNotReachHere("ResolveHandleNode int");
- } else if (HandlePointerConverter.pointsToPyFloatHandle(pointer)) {
- throw CompilerDirectives.shouldNotReachHere("ResolveHandleNode float");
- }
- HandleContext nativeContext = PythonContext.get(inliningTarget).nativeContext;
- int idx = readI32Node.read(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
- PythonObjectReference reference = nativeStubLookupGet(nativeContext, pointer, idx);
- PythonNativeWrapper wrapper = profile.profile(inliningTarget, reference.get());
- assert wrapper != null : "reference was collected: " + Long.toHexString(pointer);
- if (wrapper instanceof PythonAbstractObjectNativeWrapper objectNativeWrapper) {
- updateRefNode.execute(inliningTarget, objectNativeWrapper, objectNativeWrapper.incRef());
- }
- return wrapper;
- }
- }
-
@GenerateUncached
@GenerateInline(false)
public abstract static class CharPtrToPythonNode extends CExtToJavaNode {
+ public abstract Object execute(long pointer);
+
@Specialization
- static Object doForeign(Object value,
+ static Object doGeneric(long pointer,
@Bind Node inliningTarget,
- @CachedLibrary(limit = "3") InteropLibrary interopLibrary,
- @Cached InlinedExactClassProfile classProfile,
- @Cached InlinedConditionProfile isNullProfile,
- @Cached FromCharPointerNode fromCharPointerNode,
- @Cached ResolveHandleNode resolveHandleNode) {
- Object profiledValue = classProfile.profile(inliningTarget, value);
- // this branch is not a shortcut; it actually returns a different object
- if (isNullProfile.profile(inliningTarget, interopLibrary.isNull(profiledValue))) {
+ @Cached InlinedConditionProfile nullProfile,
+ @Cached FromCharPointerNode fromCharPointerNode) {
+ assert !HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ if (nullProfile.profile(inliningTarget, pointer == NULLPTR)) {
+ // this specialization is not a shortcut; it actually returns a different object
return PNone.NO_VALUE;
}
- log(profiledValue);
- assert !(profiledValue instanceof Long);
- if (profiledValue instanceof String) {
- return logResult(PythonUtils.toTruffleStringUncached((String) profiledValue));
- } else if (profiledValue instanceof TruffleString) {
- return logResult(profiledValue);
- }
- if (interopLibrary.isPointer(profiledValue)) {
- long pointer;
- try {
- pointer = interopLibrary.asPointer(profiledValue);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- if (HandlePointerConverter.pointsToPyHandleSpace(pointer)) {
- assert !HandlePointerConverter.pointsToPyIntHandle(pointer);
- assert !HandlePointerConverter.pointsToPyFloatHandle(pointer);
- PythonNativeWrapper obj = resolveHandleNode.execute(inliningTarget, pointer);
- if (obj != null) {
- return logResult(obj.getDelegate());
- }
- }
- }
- return logResult(fromCharPointerNode.execute(profiledValue));
+ log(pointer);
+ return logResult(fromCharPointerNode.execute(inliningTarget, pointer));
}
@NeverDefault
@@ -1442,29 +1625,62 @@ public static CharPtrToPythonNode getUncached() {
}
@GenerateUncached
- @GenerateInline(false)
- @ImportStatic({CApiGuards.class, PGuards.class})
- public abstract static class PythonToNativeNode extends CExtToNativeNode {
+ @GenerateInline
+ @GenerateCached(false)
+ @ImportStatic(CApiContext.class)
+ public abstract static class PythonToNativeInternalNode extends Node {
@TruffleBoundary
- public static Object executeUncached(Object obj) {
- return PythonToNativeNodeGen.getUncached().execute(obj);
+ public static long executeUncached(Object obj, boolean needsTransfer) {
+ return PythonToNativeInternalNodeGen.getUncached().execute(null, obj, needsTransfer);
}
- protected boolean needsTransfer() {
- return false;
+ public abstract long execute(Node inliningTarget, Object object, boolean needsTransfer);
+
+ @Specialization
+ static long doInteger(int i, @SuppressWarnings("unused") boolean needsTransfer) {
+ return HandlePointerConverter.intToPointer(i);
+ }
+
+ static boolean fitsInInt(Long l) {
+ return PInt.fitsInInt(l);
+ }
+
+ @Specialization(guards = "fitsInInt(l)")
+ static long doLong(Long l, @SuppressWarnings("unused") boolean needsTransfer) {
+ return HandlePointerConverter.intToPointer(l.intValue());
}
@Specialization
- static Object doNative(PythonAbstractNativeObject obj,
- @Bind Node inliningTarget,
- @Bind("needsTransfer()") boolean needsTransfer,
- @CachedLibrary(limit = "2") InteropLibrary lib,
- @Cached InlinedBranchProfile inlinedBranchProfile,
+ static long doFloat(Float f, @SuppressWarnings("unused") boolean needsTransfer) {
+ return HandlePointerConverter.floatToPointer(f);
+ }
+
+ static boolean fitsInFloat(Double d) {
+ return PFloat.fitsInFloat(d);
+ }
+
+ @Specialization(guards = "fitsInFloat(d)")
+ static long doDouble(Double d, @SuppressWarnings("unused") boolean needsTransfer) {
+ return HandlePointerConverter.floatToPointer(d.floatValue());
+ }
+
+ public static boolean mapsToNull(Object obj) {
+ return PNone.NO_VALUE == obj || DescriptorDeleteMarker.INSTANCE == obj;
+ }
+
+ @Specialization(guards = "mapsToNull(obj)")
+ @SuppressWarnings("unused")
+ static long doNullValues(Node inliningTarget, Object obj, boolean needsTransfer) {
+ return 0;
+ }
+
+ @Specialization
+ static long doNative(Node inliningTarget, PythonAbstractNativeObject obj, boolean needsTransfer,
+ @Exclusive @Cached InlinedBranchProfile hasReplicatedNativeReferences,
@Exclusive @Cached UpdateStrongRefNode updateRefNode) {
if (needsTransfer && PythonContext.get(inliningTarget).isNativeAccessAllowed()) {
- long ptr = PythonUtils.coerceToLong(obj.getPtr(), lib);
- long newRefcnt = CApiTransitions.addNativeRefCount(ptr, 1);
+ long newRefcnt = addNativeRefCount(obj.getPtr(), 1, false);
/*
* If a native object was only referenced from managed (i.e. refcnt ==
* MANAGED_REFCNT), it may be that its native references were already replicated to
@@ -1476,12 +1692,12 @@ static Object doNative(PythonAbstractNativeObject obj,
* referenced from native code. To avoid this, we need to update the references of
* the replicated native references.
*/
- if (newRefcnt == MANAGED_REFCNT + 1 && obj.getReplicatedNativeReferences() != null) {
- inlinedBranchProfile.enter(inliningTarget);
+ if (PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC) &&
+ newRefcnt == MANAGED_REFCNT + 1 && obj.getReplicatedNativeReferences() != null) {
+ hasReplicatedNativeReferences.enter(inliningTarget);
for (Object referent : obj.getReplicatedNativeReferences()) {
if (referent instanceof PythonObject pythonObject) {
- PythonAbstractObjectNativeWrapper nativeWrapper = pythonObject.getNativeWrapper();
- updateRefNode.execute(inliningTarget, nativeWrapper, nativeWrapper.getRefCount());
+ updateRefNode.execute(inliningTarget, pythonObject, pythonObject.getRefCount());
}
}
}
@@ -1489,65 +1705,193 @@ static Object doNative(PythonAbstractNativeObject obj,
return obj.getPtr();
}
- @Specialization
- static Object doNativePointer(NativePointer obj) {
- return obj;
- }
-
- @Specialization(guards = "mapsToNull(obj)")
- Object doNoValue(@SuppressWarnings("unused") Object obj) {
- return getContext().getNativeNull();
+ @Specialization(guards = "isSpecialSingleton(singleton)")
+ static long doSpecialSingletons(Node inliningTarget, PythonAbstractObject singleton, boolean needsTransfer) {
+ long pointer = PythonContext.get(inliningTarget).getCApiContext().getSingletonNativeWrapper(singleton);
+ assert HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ // special singletons (e.g. PNone, PEllipsis, ..) are always immortal
+ assert CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(pointer)) == IMMORTAL_REFCNT;
+ return pointer;
}
- static boolean mapsToNull(Object object) {
- return PGuards.isNoValue(object) || object instanceof DescriptorDeleteMarker;
- }
+ @Specialization
+ static long doPythonObject(Node inliningTarget, PythonObject pythonObject, boolean needsTransfer,
+ @Exclusive @Cached FirstToNativeNode firstToNativeNode,
+ @Exclusive @Cached UpdateStrongRefNode updateRefNode) {
+ CompilerAsserts.partialEvaluationConstant(needsTransfer);
+ assert PythonContext.get(inliningTarget).ownsGil();
+ pollReferenceQueue();
- static boolean isOther(Object obj) {
- return !(obj instanceof PythonAbstractNativeObject || obj instanceof NativePointer || mapsToNull(obj));
+ long pointer;
+ if (!pythonObject.isNative()) {
+ assert !CApiContext.isSpecialSingleton(pythonObject);
+ PythonContext context = PythonContext.get(inliningTarget);
+ boolean immortal = isImmortal(context, pythonObject);
+ pointer = firstToNativeNode.execute(inliningTarget, pythonObject, FirstToNativeNode.getInitialRefcnt(needsTransfer, immortal));
+ pythonObject.setNativePointer(pointer);
+ } else {
+ if (needsTransfer) {
+ /*
+ * This creates a new reference to the object and the ownership is transferred
+ * to the C extension. Therefore, we need to make the reference strong such that
+ * we do not deallocate the object if it's no longer referenced in the
+ * interpreter. The interpreter will be notified by an upcall as soon as the
+ * object's refcount goes down to MANAGED_RECOUNT again.
+ */
+ long refCnt = pythonObject.incRef();
+ assert refCnt > MANAGED_REFCNT;
+ updateRefNode.execute(inliningTarget, pythonObject, refCnt);
+ }
+ pointer = pythonObject.getNativePointer();
+ }
+ assert pythonObject.isNative();
+ assert isGcTrackedIfNecessary(pythonObject);
+ return pointer;
}
- @Specialization(guards = "isOther(obj)")
- static Object doOther(Object obj,
- @Bind("needsTransfer()") boolean needsTransfer,
- @Bind Node inliningTarget,
- @Cached GetNativeWrapperNode getWrapper,
- @Cached GetReplacementNode getReplacementNode,
- @CachedLibrary(limit = "3") InteropLibrary lib,
+ @Specialization(replaces = {"doNative", "doNullValues", "doPythonObject"})
+ static long doGeneric(Node inliningTarget, Object obj, boolean needsTransfer,
+ @Cached InlinedExactClassProfile classProfile,
+ @Exclusive @Cached InlinedBranchProfile hasReplicatedNativeReferences,
+ @Exclusive @Cached FirstToNativeNode firstToNativeNode,
@Exclusive @Cached UpdateStrongRefNode updateRefNode) {
CompilerAsserts.partialEvaluationConstant(needsTransfer);
assert PythonContext.get(inliningTarget).ownsGil();
pollReferenceQueue();
- Object wrapper = getWrapper.execute(obj);
- if (wrapper instanceof Long l) {
- return new NativePointer(l);
+
+ Object profiled = classProfile.profile(inliningTarget, obj);
+
+ // Step 1: box values in pointers if possible
+ if (profiled instanceof Integer i) {
+ return HandlePointerConverter.intToPointer(i);
+ } else if (profiled instanceof Long l && PInt.fitsInInt(l)) {
+ return HandlePointerConverter.intToPointer(l.intValue());
+ } else if (profiled instanceof Float f) {
+ return HandlePointerConverter.floatToPointer(f);
+ } else if (profiled instanceof Double d && PFloat.fitsInFloat(d)) {
+ return HandlePointerConverter.floatToPointer(d.floatValue());
}
- Object replacement = getReplacementNode.execute(inliningTarget, (PythonNativeWrapper) wrapper);
- if (replacement != null) {
- return replacement;
+ // Step 2: handle values that map to NULL
+ if (mapsToNull(profiled)) {
+ return 0;
}
- assert obj != PNone.NO_VALUE;
- if (!lib.isPointer(wrapper)) {
- lib.toNative(wrapper);
+ // Step 3: handle native objects
+ if (profiled instanceof PythonAbstractNativeObject pythonAbstractNativeObject) {
+ return doNative(inliningTarget, pythonAbstractNativeObject, needsTransfer, hasReplicatedNativeReferences, updateRefNode);
}
- if (needsTransfer && wrapper instanceof PythonAbstractObjectNativeWrapper objectNativeWrapper) {
- // native part needs to decRef to release
- long refCnt = objectNativeWrapper.incRef();
- /*
- * This creates a new reference to the object and the ownership is transferred to
- * the C extension. Therefore, we need to make the reference strong such that we do
- * not deallocate the object if it's no longer referenced in the interpreter. The
- * interpreter will be notified by an upcall as soon as the object's refcount goes
- * down to MANAGED_RECOUNT again.
- */
- assert objectNativeWrapper.ref != null;
- assert refCnt != MANAGED_REFCNT;
- updateRefNode.execute(inliningTarget, objectNativeWrapper, refCnt);
+
+ PythonContext context = PythonContext.get(inliningTarget);
+
+ /*
+ * Step 4: Special singletons (e.g. PNone) are context-independent. Their native
+ * companions are stored in a special cache.
+ */
+ long pointer;
+ if (CApiContext.isSpecialSingleton(profiled)) {
+ pointer = context.getCApiContext().getSingletonNativeWrapper((PythonAbstractObject) profiled);
+ assert HandlePointerConverter.pointsToPyHandleSpace(pointer);
+ // special singletons (e.g. PNone, PEllipsis, ..) are always immortal
+ assert CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(pointer)) == IMMORTAL_REFCNT;
+ return pointer;
+ }
+
+ /*
+ * Step 5: At this point, all objects are expected to be PythonObject. If not, then the
+ * caller needs to promote the object. We cannot do it here because the promoted object
+ * is then not kept alive and this may lead to deallocation.
+ */
+ PythonObject pythonObject = (PythonObject) profiled;
+ assert !CApiContext.isSpecialSingleton(pythonObject);
+
+ /*
+ * Step 6: If the PythonObject is not already native, create the native companion. If it
+ * already has a native companion, use it and maybe update the reference (strong/weak)
+ * if the reference count is increased.
+ */
+ if (!pythonObject.isNative()) {
+ boolean immortal = isImmortalPythonObject(context, pythonObject);
+ pointer = firstToNativeNode.execute(inliningTarget, pythonObject, FirstToNativeNode.getInitialRefcnt(needsTransfer, immortal));
+ pythonObject.setNativePointer(pointer);
+ } else {
+ if (needsTransfer) {
+ /*
+ * This creates a new reference to the object and the ownership is transferred
+ * to the C extension. Therefore, we need to make the reference strong such that
+ * we do not deallocate the object if it's no longer referenced in the
+ * interpreter. The interpreter will be notified by an upcall as soon as the
+ * object's refcount goes down to MANAGED_RECOUNT again.
+ */
+ long refCnt = pythonObject.incRef();
+ assert refCnt > MANAGED_REFCNT;
+ updateRefNode.execute(inliningTarget, pythonObject, refCnt);
+ }
+ pointer = pythonObject.getNativePointer();
+ }
+ assert isGcTrackedIfNecessary(pythonObject);
+ assert pythonObject.isNative();
+ return pointer;
+ }
+
+ /**
+ * Determines if an arbitrary object is immortal.
+ */
+ public static boolean isImmortal(PythonContext context, Object object) {
+ // boxable int/float values and any special singletons are immortal
+ return object instanceof Integer ||
+ object instanceof Long l && PInt.fitsInInt(l) ||
+ object instanceof Float ||
+ object instanceof Double d && PFloat.fitsInFloat(d) ||
+ CApiContext.isSpecialSingleton(object) ||
+ object instanceof PythonObject pythonObject && isImmortalPythonObject(context, pythonObject);
+ }
+
+ /**
+ * PMemoryView, static native classes, built-in classes, and singletons True/False are
+ * immortal.
+ */
+ private static boolean isImmortalPythonObject(PythonContext context, PythonObject pythonObject) {
+ return pythonObject instanceof PythonBuiltinClass || context.getTrue() == pythonObject || context.getFalse() == pythonObject || pythonObject instanceof PMemoryView;
+ }
+
+ /**
+ * Verify, if the object is correctly tracked by the Python GC if necessary.
+ */
+ @TruffleBoundary
+ private static boolean isGcTrackedIfNecessary(PythonObject object) {
+ // if Python GC is not enabled, we are fine
+ if (!PythonLanguage.get(null).getEngineOption(PythonOptions.PythonGC)) {
+ return true;
+ }
+ // an object won't be GC tracked if the reference is "weak" or if it is immortal
+ long refCount = object.getRefCount();
+ if (refCount == MANAGED_REFCNT || refCount == IMMORTAL_REFCNT) {
+ return true;
}
- assert wrapper != null;
- return wrapper;
+
+ // is_gc(type(delegate)) => tracked
+ boolean isGc = (GetTypeFlagsNode.executeUncached(GetClassNode.executeUncached(object)) & TypeFlags.HAVE_GC) != 0;
+ return !isGc || PyObjectGCTrackNode.isGcTracked(object.getNativePointer());
+ }
+ }
+
+ @GenerateUncached
+ @GenerateInline(false)
+ public abstract static class PythonToNativeNode extends CExtToNativeNode {
+
+ public abstract long executeLong(Object object);
+
+ @TruffleBoundary
+ public static long executeLongUncached(Object obj) {
+ return getUncached().executeLong(obj);
+ }
+
+ @Specialization
+ static long doGeneric(Object obj,
+ @Bind Node inliningTarget,
+ @Cached PythonToNativeInternalNode internalNode) {
+ return internalNode.execute(inliningTarget, obj, false);
}
@NeverDefault
@@ -1589,22 +1933,26 @@ public static PythonToNativeNode getUncached() {
*/
@GenerateUncached
@GenerateInline(false)
- public abstract static class PythonToNativeNewRefNode extends PythonToNativeNode {
+ public abstract static class PythonToNativeNewRefNode extends CExtToNativeNode {
- @Specialization
- static Object dummy(@SuppressWarnings("unused") Void dummy) {
- // needed for DSL (GR-44728)
- throw CompilerDirectives.shouldNotReachHere();
- }
+ public abstract long executeLong(Object object);
@TruffleBoundary
- public static Object executeUncached(Object obj) {
- return PythonToNativeNewRefNodeGen.getUncached().execute(obj);
+ public static long executeLongUncached(Object obj) {
+ return getUncached().executeLong(obj);
}
- @Override
- protected final boolean needsTransfer() {
- return true;
+ @Specialization
+ static long doGeneric(Object obj,
+ @Bind Node inliningTarget,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached PythonToNativeInternalNode internalNode) {
+ /*
+ * In case of a new reference, we can promote the object here (if necessary) because it
+ * will be kept alive with a strong reference from the handle table.
+ */
+ Object promoted = ensurePythonObjectNode.execute(PythonContext.get(inliningTarget), obj, false);
+ return internalNode.execute(inliningTarget, promoted, true);
}
@NeverDefault
@@ -1618,78 +1966,54 @@ public static PythonToNativeNewRefNode getUncached() {
}
@GenerateUncached
- @GenerateInline(false)
- @ImportStatic(CApiGuards.class)
- public abstract static class NativeToPythonNode extends CExtToJavaNode {
-
- public abstract Object execute(PythonNativeWrapper object);
+ @GenerateInline
+ @GenerateCached(false)
+ public abstract static class NativeToPythonInternalNode extends Node {
- @TruffleBoundary
- public static Object executeUncached(Object obj) {
- return NativeToPythonNodeGen.getUncached().execute(obj);
+ public final Object execute(Node inliningTarget, long value, boolean needsTransfer) {
+ return execute(inliningTarget, value, needsTransfer, false);
}
- protected boolean needsTransfer() {
- return false;
- }
+ public abstract Object execute(Node inliningTarget, long value, boolean needsTransfer, boolean release);
- @Specialization
- static Object doWrapper(PythonNativeWrapper value,
- @Bind Node inliningTarget,
- @Exclusive @Cached InlinedExactClassProfile wrapperProfile,
- @Exclusive @Cached UpdateStrongRefNode updateRefNode) {
- return handleWrapper(inliningTarget, wrapperProfile, updateRefNode, false, value);
+ @TruffleBoundary
+ public static Object executeUncached(long value, boolean needsTransfer) {
+ return NativeToPythonInternalNodeGen.getUncached().execute(null, value, needsTransfer);
}
- @Specialization(guards = "!isNativeWrapper(value)", limit = "3")
+ @Specialization
@SuppressWarnings({"truffle-static-method", "truffle-sharing"})
- Object doNonWrapper(Object value,
- @Bind Node inliningTarget,
- @CachedLibrary("value") InteropLibrary interopLibrary,
- @Cached CStructAccess.ReadI32Node readI32Node,
- @Cached InlinedConditionProfile isNullProfile,
+ static Object doGeneric(Node inliningTarget, long pointer, boolean needsTransfer, boolean release,
@Cached InlinedConditionProfile isZeroProfile,
@Cached InlinedConditionProfile createNativeProfile,
@Cached InlinedConditionProfile isNativeProfile,
- @Cached InlinedConditionProfile isNativeWrapperProfile,
+ @Cached InlinedConditionProfile isNativeObjectProfile,
@Cached InlinedConditionProfile isHandleSpaceProfile,
@Exclusive @Cached InlinedExactClassProfile wrapperProfile,
@Exclusive @Cached UpdateStrongRefNode updateRefNode) {
- assert !(value instanceof TruffleString);
- assert !(value instanceof PythonAbstractObject);
- assert !(value instanceof Number);
-
- // this is just a shortcut
- if (isNullProfile.profile(inliningTarget, interopLibrary.isNull(value))) {
+ if (isZeroProfile.profile(inliningTarget, pointer == 0)) {
return PNone.NO_VALUE;
}
- PythonNativeWrapper wrapper;
PythonContext pythonContext = PythonContext.get(inliningTarget);
- HandleContext nativeContext = pythonContext.nativeContext;
+ HandleContext handleContext = pythonContext.handleContext;
- if (!interopLibrary.isPointer(value)) {
- return getManagedReference(value, nativeContext);
- }
- long pointer;
- try {
- pointer = interopLibrary.asPointer(value);
- } catch (final UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- if (isZeroProfile.profile(inliningTarget, pointer == 0)) {
- return PNone.NO_VALUE;
- }
assert pythonContext.ownsGil();
+ PythonAbstractObject result;
if (isHandleSpaceProfile.profile(inliningTarget, HandlePointerConverter.pointsToPyHandleSpace(pointer))) {
if (HandlePointerConverter.pointsToPyIntHandle(pointer)) {
return HandlePointerConverter.pointerToLong(pointer);
} else if (HandlePointerConverter.pointsToPyFloatHandle(pointer)) {
return HandlePointerConverter.pointerToDouble(pointer);
}
- int idx = readI32Node.read(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
- PythonObjectReference reference = nativeStubLookupGet(nativeContext, pointer, idx);
- if (reference == null) {
+ int idx = readIntField(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
+ Object reference = nativeStubLookupGet(handleContext, pointer, idx);
+ if (reference instanceof PythonAbstractObject) {
+ result = (PythonAbstractObject) reference;
+ } else if (reference instanceof PythonObjectReference pythonObjectReference) {
+ result = pythonObjectReference.get();
+ } else {
+ assert reference == null;
/*
* Here we are encountering a weakref object that has died in the managed side,
* e.g. PReferenceType, but we kept alive in the native side, see
@@ -1702,9 +2026,8 @@ Object doNonWrapper(Object value,
}
return PNone.NO_VALUE;
}
- wrapper = reference.get();
- if (wrapper == null) {
- int collecting = readI32Node.read(pythonContext.getCApiContext().getGCState(), CFields.GCState__collecting);
+ if (result == null) {
+ int collecting = CStructAccess.readIntField(pythonContext.getCApiContext().getGCState(), CFields.GCState__collecting);
if (collecting == 1) {
return PNone.NO_VALUE;
}
@@ -1712,43 +2035,45 @@ Object doNonWrapper(Object value,
throw CompilerDirectives.shouldNotReachHere("reference was collected: " + Long.toHexString(pointer));
}
} else {
- IdReference> lookup = nativeLookupGet(nativeContext, pointer);
+ IdReference> lookup = nativeLookupGet(handleContext, pointer);
+ PythonThreadState threadState = pythonContext.getThreadState(pythonContext.getLanguage());
if (isNativeProfile.profile(inliningTarget, lookup != null)) {
Object ref = lookup.get();
if (createNativeProfile.profile(inliningTarget, ref == null)) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(() -> "re-creating collected PythonAbstractNativeObject reference" + Long.toHexString(pointer));
}
- return createAbstractNativeObject(nativeContext, value, needsTransfer(), pointer);
+ return createAbstractNativeObject(threadState, handleContext, needsTransfer, pointer);
}
- if (isNativeWrapperProfile.profile(inliningTarget, ref instanceof PythonNativeWrapper)) {
- wrapper = (PythonNativeWrapper) ref;
- } else {
- PythonAbstractNativeObject result = (PythonAbstractNativeObject) ref;
- if (needsTransfer()) {
- addNativeRefCount(pointer, -1);
+ if (isNativeObjectProfile.profile(inliningTarget, ref instanceof PythonAbstractNativeObject)) {
+ if (needsTransfer) {
+ subNativeRefCount(pointer, 1);
}
- return result;
+ return ref;
+ } else {
+ assert ref instanceof PythonAbstractObject;
+ result = (PythonAbstractObject) ref;
}
} else {
- return createAbstractNativeObject(nativeContext, value, needsTransfer(), pointer);
+ return createAbstractNativeObject(threadState, handleContext, needsTransfer, pointer);
}
}
- return handleWrapper(inliningTarget, wrapperProfile, updateRefNode, needsTransfer(), wrapper);
+ return updateRef(inliningTarget, wrapperProfile, updateRefNode, needsTransfer, release, result);
}
/**
- * Resolves a wrapper to its delegate and does appropriate reference count manipulation.
+ * Resolves a object to its delegate and does appropriate reference count manipulation.
*
* @param node The inlining target for profiles.
- * @param wrapperProfile The wrapper class profile.
+ * @param wrapperProfile The object class profile.
* @param transfer Indicates if ownership of the reference is transferred to managed space.
- * @param wrapper The native wrapper to unwrap.
- * @return The Python value contained in the native wrapper.
+ * @param object The native object to unwrap.
+ * @return The Python value contained in the native object.
*/
- static Object handleWrapper(Node node, InlinedExactClassProfile wrapperProfile, UpdateStrongRefNode updateRefNode, boolean transfer, PythonNativeWrapper wrapper) {
- PythonNativeWrapper profiledWrapper = wrapperProfile.profile(node, wrapper);
- if (transfer && profiledWrapper instanceof PythonAbstractObjectNativeWrapper objectNativeWrapper) {
+ static PythonAbstractObject updateRef(Node node, InlinedExactClassProfile wrapperProfile, UpdateStrongRefNode updateRefNode, boolean transfer, boolean release, PythonAbstractObject object) {
+ PythonAbstractObject profiled = wrapperProfile.profile(node, object);
+ assert !(profiled instanceof PythonAbstractNativeObject);
+ if (transfer && profiled instanceof PythonObject pythonObject) {
/*
* If 'transfer' is true, this means the ownership is transferred (in this case to
* the "interpreter"). We don't do reference counting in the interpreter, therefore
@@ -1759,37 +2084,34 @@ static Object handleWrapper(Node node, InlinedExactClassProfile wrapperProfile,
* *MUST* have done an incref and so the refcount must be greater than
* MANAGED_REFCNT.
*/
- assert objectNativeWrapper.getRefCount() > MANAGED_REFCNT;
- updateRefNode.execute(node, objectNativeWrapper, objectNativeWrapper.decRef());
- }
- if (profiledWrapper instanceof PrimitiveNativeWrapper primitive) {
- if (primitive.isBool()) {
- return primitive.getBool();
- } else if (primitive.isInt()) {
- return primitive.getInt();
- } else if (primitive.isLong()) {
- return primitive.getLong();
- } else if (primitive.isDouble()) {
- return primitive.getDouble();
- } else {
- throw CompilerDirectives.shouldNotReachHere();
- }
- } else {
- return wrapper.getDelegate();
+ assert pythonObject.getRefCount() > MANAGED_REFCNT;
+ updateRefNode.execute(node, pythonObject, pythonObject.decRef(), release);
}
+ return object;
}
+ }
+
+ @GenerateUncached
+ @GenerateInline(false)
+ public abstract static class NativeToPythonNode extends CExtToJavaNode {
+
+ public abstract Object executeRaw(long pointer);
@TruffleBoundary
- private static Object getManagedReference(Object value, HandleContext nativeContext) {
- assert value.toString().startsWith("ManagedMemoryBlock");
- assert PythonContext.get(null).ownsGil();
- WeakReference ref = nativeContext.managedNativeLookup.computeIfAbsent(value, o -> new WeakReference<>(new PythonAbstractNativeObject(o)));
- Object result = ref.get();
- if (result == null) {
- // value is weak as well:
- nativeContext.managedNativeLookup.put(value, new WeakReference<>(result = new PythonAbstractNativeObject(value)));
- }
- return result;
+ public static Object executeRawUncached(long pointer) {
+ return NativeToPythonNodeGen.getUncached().executeRaw(pointer);
+ }
+
+ @TruffleBoundary
+ public static Object executeUncached(Object obj) {
+ return NativeToPythonNodeGen.getUncached().execute(obj);
+ }
+
+ @Specialization
+ static Object doLong(long value,
+ @Bind Node inliningTarget,
+ @Shared @Cached NativeToPythonInternalNode nativeToPythonInternalNode) {
+ return nativeToPythonInternalNode.execute(inliningTarget, value, false);
}
@NeverDefault
@@ -1804,22 +2126,20 @@ public static NativeToPythonNode getUncached() {
@GenerateUncached
@GenerateInline(false)
- public abstract static class NativeToPythonTransferNode extends NativeToPythonNode {
+ public abstract static class NativeToPythonTransferNode extends CExtToJavaNode {
- @Specialization
- static Object dummy(@SuppressWarnings("unused") Void dummy) {
- // needed for DSL (GR-44728)
- throw CompilerDirectives.shouldNotReachHere();
- }
+ public abstract Object executeRaw(long pointer);
@TruffleBoundary
- public static Object executeUncached(Object obj) {
- return NativeToPythonTransferNodeGen.getUncached().execute(obj);
+ public static Object executeRawUncached(long pointer) {
+ return NativeToPythonTransferNodeGen.getUncached().executeRaw(pointer);
}
- @Override
- protected final boolean needsTransfer() {
- return true;
+ @Specialization
+ static Object doLong(long pointer,
+ @Bind Node inliningTarget,
+ @Shared @Cached NativeToPythonInternalNode nativeToPythonInternalNode) {
+ return nativeToPythonInternalNode.execute(inliningTarget, pointer, true);
}
@NeverDefault
@@ -1834,7 +2154,46 @@ public static NativeToPythonTransferNode getUncached() {
@GenerateUncached
@GenerateInline(false)
- @ImportStatic(CApiGuards.class)
+ public abstract static class NativeToPythonReturnNode extends CExtToJavaNode {
+
+ public abstract Object executeRaw(long pointer);
+
+ @TruffleBoundary
+ public static Object executeUncached(long pointer) {
+ return NativeToPythonReturnNodeGen.getUncached().execute(pointer);
+ }
+
+ @Specialization
+ static Object doLong(long pointer,
+ @Bind Node inliningTarget,
+ @Shared @Cached NativeToPythonInternalNode nativeToPythonInternalNode) {
+ return nativeToPythonInternalNode.execute(inliningTarget, pointer, true, true);
+ }
+
+ @Specialization(limit = "1")
+ static Object doNativePointer(Object pointer,
+ @Bind Node inliningTarget,
+ @CachedLibrary("pointer") InteropLibrary lib,
+ @Shared @Cached NativeToPythonInternalNode nativeToPythonInternalNode) {
+ try {
+ return doLong(lib.asPointer(pointer), inliningTarget, nativeToPythonInternalNode);
+ } catch (UnsupportedMessageException e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ }
+
+ @NeverDefault
+ public static NativeToPythonReturnNode create() {
+ return NativeToPythonReturnNodeGen.create();
+ }
+
+ public static NativeToPythonReturnNode getUncached() {
+ return NativeToPythonReturnNodeGen.getUncached();
+ }
+ }
+
+ @GenerateUncached
+ @GenerateInline(false)
public abstract static class NativePtrToPythonNode extends PNodeWithContext {
public abstract Object execute(long object, boolean stealing);
@@ -1846,23 +2205,22 @@ public static Object executeUncached(long object, boolean stealing) {
@Specialization
@SuppressWarnings({"truffle-static-method", "truffle-sharing"})
- Object doNonWrapper(long pointer, boolean stealing,
+ static Object doNonWrapper(long pointer, boolean stealing,
@Bind Node inliningTarget,
- @Cached CStructAccess.ReadI32Node readI32Node,
@Cached InlinedConditionProfile isZeroProfile,
@Cached InlinedConditionProfile createNativeProfile,
@Cached InlinedConditionProfile isNativeProfile,
- @Cached InlinedConditionProfile isNativeWrapperProfile,
+ @Cached InlinedConditionProfile isNativeObjectProfile,
@Cached InlinedConditionProfile isHandleSpaceProfile,
@Cached InlinedExactClassProfile wrapperProfile,
@Cached UpdateStrongRefNode updateRefNode) {
assert PythonContext.get(null).ownsGil();
CompilerAsserts.partialEvaluationConstant(stealing);
- PythonNativeWrapper wrapper;
+ PythonAbstractObject pythonAbstractObject;
PythonContext pythonContext = PythonContext.get(inliningTarget);
- HandleContext nativeContext = pythonContext.nativeContext;
+ HandleContext handleContext = pythonContext.handleContext;
if (isZeroProfile.profile(inliningTarget, pointer == 0)) {
return PNone.NO_VALUE;
@@ -1874,39 +2232,44 @@ Object doNonWrapper(long pointer, boolean stealing,
} else if (HandlePointerConverter.pointsToPyFloatHandle(pointer)) {
return HandlePointerConverter.pointerToDouble(pointer);
}
- int idx = readI32Node.read(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
- PythonObjectReference reference = nativeStubLookupGet(nativeContext, pointer, idx);
- if (reference == null) {
+ int idx = readIntField(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
+ Object reference = nativeStubLookupGet(handleContext, pointer, idx);
+ if (reference instanceof PythonAbstractObject) {
+ pythonAbstractObject = (PythonAbstractObject) reference;
+ } else if (reference instanceof PythonObjectReference pythonObjectReference) {
+ pythonAbstractObject = pythonObjectReference.get();
+ } else {
+ assert reference == null;
CompilerDirectives.transferToInterpreterAndInvalidate();
throw CompilerDirectives.shouldNotReachHere("reference was freed: " + Long.toHexString(pointer));
}
- wrapper = reference.get();
- if (wrapper == null) {
+ if (pythonAbstractObject == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw CompilerDirectives.shouldNotReachHere("reference was collected: " + Long.toHexString(pointer));
}
} else {
- IdReference> lookup = nativeLookupGet(nativeContext, pointer);
+ IdReference> lookup = nativeLookupGet(handleContext, pointer);
+ PythonThreadState threadState = pythonContext.getThreadState(pythonContext.getLanguage());
if (isNativeProfile.profile(inliningTarget, lookup != null)) {
Object ref = lookup.get();
if (createNativeProfile.profile(inliningTarget, ref == null)) {
LOGGER.fine(() -> "re-creating collected PythonAbstractNativeObject reference" + Long.toHexString(pointer));
- return createAbstractNativeObject(nativeContext, new NativePointer(pointer), stealing, pointer);
+ return createAbstractNativeObject(threadState, handleContext, stealing, pointer);
}
- if (isNativeWrapperProfile.profile(inliningTarget, ref instanceof PythonNativeWrapper)) {
- wrapper = (PythonNativeWrapper) ref;
- } else {
- PythonAbstractNativeObject result = (PythonAbstractNativeObject) ref;
+ if (isNativeObjectProfile.profile(inliningTarget, ref instanceof PythonAbstractNativeObject)) {
if (stealing) {
- addNativeRefCount(pointer, -1);
+ subNativeRefCount(pointer, 1);
}
- return result;
+ return ref;
+ } else {
+ assert ref instanceof PythonAbstractObject;
+ pythonAbstractObject = (PythonAbstractObject) ref;
}
} else {
- return createAbstractNativeObject(nativeContext, new NativePointer(pointer), stealing, pointer);
+ return createAbstractNativeObject(threadState, handleContext, stealing, pointer);
}
}
- return NativeToPythonNode.handleWrapper(inliningTarget, wrapperProfile, updateRefNode, stealing, wrapper);
+ return NativeToPythonInternalNode.updateRef(inliningTarget, wrapperProfile, updateRefNode, stealing, false, pythonAbstractObject);
}
}
@@ -1925,17 +2288,20 @@ Object doNonWrapper(long pointer, boolean stealing,
@GenerateCached(false)
public abstract static class GcNativePtrToPythonNode extends PNodeWithContext {
+ @TruffleBoundary
+ public static Object executeUncached(long pointer) {
+ return CApiTransitionsFactory.GcNativePtrToPythonNodeGen.getUncached().execute(null, pointer);
+ }
+
public abstract Object execute(Node inliningTarget, long pointer);
@Specialization
static Object doLong(Node inliningTarget, long pointer,
- @Cached(inline = false) CStructAccess.ReadI32Node readI32Node,
@Cached InlinedBranchProfile isNativeProfile,
- @Cached InlinedConditionProfile isNativeWrapperProfile,
@Cached InlinedConditionProfile isHandleSpaceProfile) {
PythonContext pythonContext = PythonContext.get(inliningTarget);
- HandleContext nativeContext = pythonContext.nativeContext;
+ HandleContext handleContext = pythonContext.handleContext;
assert pointer != 0;
assert pythonContext.ownsGil();
@@ -1945,9 +2311,15 @@ static Object doLong(Node inliningTarget, long pointer,
} else if (HandlePointerConverter.pointsToPyFloatHandle(pointer)) {
return HandlePointerConverter.pointerToDouble(pointer);
}
- int idx = readI32Node.read(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
- PythonObjectReference reference = nativeStubLookupGet(nativeContext, pointer, idx);
- if (reference == null) {
+ int idx = readIntField(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
+ PythonObject pythonObject;
+ Object reference = nativeStubLookupGet(handleContext, pointer, idx);
+ if (reference instanceof PythonObject) {
+ pythonObject = (PythonObject) reference;
+ } else if (reference instanceof PythonObjectReference pythonObjectReference) {
+ pythonObject = pythonObjectReference.get();
+ } else {
+ assert reference == null;
/*
* This should really not happen since it most likely means that we accessed
* free'd memory to read the handle table index.
@@ -1955,31 +2327,159 @@ static Object doLong(Node inliningTarget, long pointer,
CompilerDirectives.transferToInterpreterAndInvalidate();
throw CompilerDirectives.shouldNotReachHere("reference was freed: " + Long.toHexString(pointer));
}
- PythonNativeWrapper wrapper = reference.get();
- return wrapper != null ? wrapper.getDelegate() : null;
+ return pythonObject;
} else {
- IdReference> lookup = nativeLookupGet(nativeContext, pointer);
+ IdReference> lookup = nativeLookupGet(handleContext, pointer);
Object referent;
if (lookup != null && (referent = lookup.get()) != null) {
isNativeProfile.enter(inliningTarget);
- if (isNativeWrapperProfile.profile(inliningTarget, referent instanceof PythonAbstractObjectNativeWrapper)) {
- assert referent instanceof PythonAbstractObjectNativeWrapper;
- return ((PythonAbstractObjectNativeWrapper) referent).getDelegate();
- } else {
- assert referent instanceof PythonAbstractNativeObject;
- return referent;
- }
+ assert referent instanceof PythonAbstractNativeObject;
+ return referent;
}
return null;
}
}
}
+ /**
+ * Resolves a native pointer of type {@code PyTypeObject *} to a {@link PythonAbstractClass}
+ * object or {@link PNone#NO_VALUE}. This node is semantically equivalent to
+ * {@link NativeToPythonInternalNode} but uses a fast index-based lookup. This node cannot
+ * transfer ownership of the reference (i.e. won't manipulate the refcount).
+ */
+ @GenerateUncached
+ @GenerateInline
+ @GenerateCached(false)
+ public abstract static class NativeToPythonClassInternalNode extends Node {
+
+ @TruffleBoundary
+ public static Object executeUncached(long value) {
+ return NativeToPythonClassInternalNodeGen.getUncached().execute(null, value);
+ }
+
+ public abstract Object execute(Node inliningTarget, long value);
+
+ @Specialization
+ static Object doGeneric(Node inliningTarget, long pointer,
+ @Cached InlinedConditionProfile isZeroProfile,
+ @Cached InlinedBranchProfile createNativeProfile) {
+ if (isZeroProfile.profile(inliningTarget, pointer == 0)) {
+ return PNone.NO_VALUE;
+ }
+
+ PythonContext pythonContext = PythonContext.get(inliningTarget);
+ HandleContext handleContext = pythonContext.handleContext;
+
+ assert pythonContext.ownsGil();
+ IdReference> lookup;
+
+ int typeLookupTableIdx = readIntField(pointer, CFields.PyTypeObject__tp_version_tag);
+ if (typeLookupTableIdx != 0) {
+ // fast index-based lookup
+ lookup = nativeTypeLookupGet(handleContext, pointer, typeLookupTableIdx);
+ } else {
+ // generic HashMap-based lookup
+ lookup = nativeLookupGet(handleContext, pointer);
+ }
+
+ Object clazz;
+ if (lookup == null || (clazz = lookup.get()) == null) {
+ createNativeProfile.enter(inliningTarget);
+ // this may only happen for native types otherwise we are using a dangling pointer
+ assert lookup == null || lookup instanceof NativeObjectReference;
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("re-creating collected PythonNativeClass reference 0x%x", pointer));
+ }
+ return recreatePythonNativeClass(handleContext, pointer);
+ }
+ assert clazz instanceof PythonAbstractClass;
+ return clazz;
+ }
+ }
+
+ @GenerateUncached
+ @GenerateInline(false)
+ public abstract static class NativeToPythonClassNode extends CExtToJavaNode {
+
+ @TruffleBoundary
+ public static Object executeUncached(Object obj) {
+ return NativeToPythonClassNodeGen.getUncached().execute(obj);
+ }
+
+ @Specialization
+ static Object doLong(long value,
+ @Bind Node inliningTarget,
+ @Shared @Cached NativeToPythonClassInternalNode nativeToPythonInternalNode) {
+ return nativeToPythonInternalNode.execute(inliningTarget, value);
+ }
+
+ @Specialization(limit = "1")
+ static Object doInteropPointer(Object nativePointer,
+ @Bind Node inliningTarget,
+ @Shared @Cached NativeToPythonClassInternalNode nativeToPythonInternalNode,
+ @CachedLibrary("nativePointer") InteropLibrary lib) {
+ try {
+ return nativeToPythonInternalNode.execute(inliningTarget, lib.asPointer(nativePointer));
+ } catch (UnsupportedMessageException e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ }
+
+ @NeverDefault
+ public static NativeToPythonClassNode create() {
+ return NativeToPythonClassNodeGen.create();
+ }
+
+ public static NativeToPythonClassNode getUncached() {
+ return NativeToPythonClassNodeGen.getUncached();
+ }
+ }
+
private static final Unsafe UNSAFE = PythonUtils.initUnsafe();
private static final int TP_REFCNT_OFFSET = 0;
+ private static long tstateGraalpyDeallocatingOffset = -1;
+ private static long graalpyDeallocatingItemsOffset = -1;
+ private static long graalpyDeallocatingLenOffset = -1;
+ private static long graalpyDeallocatingCapacityOffset = -1;
- public static long addNativeRefCount(long pointer, long refCntDelta) {
- return addNativeRefCount(pointer, refCntDelta, false);
+ public static void initializeThreadStateDeallocatingOffsets() {
+ if (tstateGraalpyDeallocatingOffset == -1) {
+ tstateGraalpyDeallocatingOffset = CFields.PyThreadState__graalpy_deallocating.offset();
+ graalpyDeallocatingItemsOffset = CFields.GraalPyDeallocState__items.offset();
+ graalpyDeallocatingLenOffset = CFields.GraalPyDeallocState__len.offset();
+ graalpyDeallocatingCapacityOffset = CFields.GraalPyDeallocState__capacity.offset();
+ }
+ }
+
+ @TruffleBoundary
+ private static boolean isDeallocatingSlowPath(long pointer) {
+ PythonContext context = PythonContext.get(null);
+ PythonThreadState threadState = context.getThreadState(context.getLanguage());
+ if (!threadState.isNativeThreadStateInitialized()) {
+ return false;
+ }
+ long threadStatePointer = threadState.getNativePointer();
+ return isDeallocating(pointer, threadStatePointer);
+ }
+
+ private static boolean isDeallocating(long pointer, long threadStatePointer) {
+ assert tstateGraalpyDeallocatingOffset != -1 : "thread-state deallocating offsets not initialized";
+ assert threadStatePointer != 0;
+ long deallocatingState = threadStatePointer + tstateGraalpyDeallocatingOffset;
+ int length = UNSAFE.getInt(deallocatingState + graalpyDeallocatingLenOffset);
+ if (length <= 0) {
+ return false;
+ }
+ int capacity = UNSAFE.getInt(deallocatingState + graalpyDeallocatingCapacityOffset);
+ assert length <= capacity : PythonUtils.formatJString("invalid deallocating stack size for tstate 0x%x (%d/%d)", threadStatePointer, length, capacity);
+ long deallocatingArray = UNSAFE.getAddress(deallocatingState + graalpyDeallocatingItemsOffset);
+ assert deallocatingArray != 0;
+ for (int i = 0; i < length; i++) {
+ if (UNSAFE.getAddress(deallocatingArray + i * NativeMemory.POINTER_SIZE) == pointer) {
+ return true;
+ }
+ }
+ return false;
}
private static long addNativeRefCount(long pointer, long refCntDelta, boolean ignoreIfDead) {
@@ -1998,6 +2498,7 @@ private static long addNativeRefCount(long pointer, long refCntDelta, boolean ig
LOGGER.finest(() -> PythonUtils.formatJString("addNativeRefCount %x %x %d + %d", pointer, refCount, refCount, refCntDelta));
+ assert !isDeallocatingSlowPath(pointer);
UNSAFE.putLong(pointer + TP_REFCNT_OFFSET, refCount + refCntDelta);
return refCount + refCntDelta;
}
@@ -2005,6 +2506,7 @@ private static long addNativeRefCount(long pointer, long refCntDelta, boolean ig
public static long subNativeRefCount(long pointer, long refCntDelta) {
assert PythonContext.get(null).isNativeAccessAllowed();
assert PythonContext.get(null).ownsGil();
+ assert !isDeallocatingSlowPath(pointer);
long refCount = UNSAFE.getLong(pointer + TP_REFCNT_OFFSET);
if (refCount == IMMORTAL_REFCNT) {
return IMMORTAL_REFCNT;
@@ -2039,23 +2541,53 @@ public static void writeNativeRefCount(long pointer, long newValue) {
UNSAFE.putLong(pointer + TP_REFCNT_OFFSET, newValue);
}
- private static Object createAbstractNativeObject(HandleContext handleContext, Object obj, boolean transfer, long pointer) {
- assert isBackendPointerObject(obj) : obj.getClass();
-
+ private static PythonAbstractNativeObject createAbstractNativeObject(PythonThreadState threadState, HandleContext handleContext, boolean transfer, long pointer) {
pollReferenceQueue();
- PythonAbstractNativeObject result = new PythonAbstractNativeObject(obj);
+ PythonAbstractNativeObject result = new PythonAbstractNativeObject(pointer);
long refCntDelta = MANAGED_REFCNT - (transfer ? 1 : 0);
+ /*
+ * Some APIs might be called from tp_dealloc/tp_del/tp_finalize while the native object is
+ * already dying. In that case we don't want to create a new reference, since that would
+ * resurrect the object and we would end up deallocating it twice.
+ */
+ boolean isDeallocating = threadState.isNativeThreadStateInitialized() && isDeallocating(pointer, threadState.getNativePointer());
+ if (!isDeallocating && addNativeRefCount(pointer, refCntDelta, true) > 0) {
+ NativeObjectReference ref = new NativeObjectReference(handleContext, result, pointer);
+ nativeLookupPut(handleContext, pointer, ref);
+ } else if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("createAbstractNativeObject: creating PythonAbstractNativeObject for a dying object (reason: %s): 0x%x",
+ isDeallocating ? "deallocating" : "refcount 0", pointer));
+ }
+
+ return result;
+ }
+
+ /**
+ * Re-creates a {@link PythonNativeClass} that died because there were no more references from
+ * the managed side. This method is similar to {@link #createAbstractNativeObject} but assumes
+ * that the pointer is of type {@link CStructs#PyTypeObject} and will also set up the fast type
+ * lookup. It attempts to reuse the slot in the native type lookup table by reading the existing
+ * index from the native {@code PyTypeObject}.
+ */
+ @TruffleBoundary
+ private static PythonNativeClass recreatePythonNativeClass(HandleContext handleContext, long pointer) {
+ pollReferenceQueue();
+ PythonAbstractNativeObject result = new PythonAbstractNativeObject(pointer);
/*
* Some APIs might be called from tp_dealloc/tp_del/tp_finalize where the refcount is 0. In
* that case we don't want to create a new reference, since that would resurrect the object
* and we would end up deallocating it twice.
*/
- long refCount = addNativeRefCount(pointer, refCntDelta, true);
+ long refCount = addNativeRefCount(pointer, MANAGED_REFCNT, true);
if (refCount > 0) {
NativeObjectReference ref = new NativeObjectReference(handleContext, result, pointer);
- nativeLookupPut(getContext(), pointer, ref);
+ nativeLookupPut(handleContext, pointer, ref);
+ int typeLookupIdx = readIntField(pointer, CFields.PyTypeObject__tp_version_tag);
+ if (typeLookupIdx != 0) {
+ nativeTypeLookupUpdate(handleContext, typeLookupIdx, ref);
+ }
} else if (LOGGER.isLoggable(Level.FINE)) {
- LOGGER.fine(PythonUtils.formatJString("createAbstractNativeObject: creating PythonAbstractNativeObject for a dying object (refcount 0): 0x%x", pointer));
+ LOGGER.fine(PythonUtils.formatJString("createPythonNativeClass: creating PythonNativeClass for a dying object (refcount 0): 0x%x", pointer));
}
return result;
@@ -2066,148 +2598,86 @@ public static boolean isBackendPointerObject(Object obj) {
return obj != null && (obj.getClass().toString().contains("LLVMPointerImpl") || obj.getClass().toString().contains("NFIPointer") || obj.getClass().toString().contains("NativePointer"));
}
+ /**
+ * Same as {@link UpdateHandleTableReferenceNode} but the handle table reference is directly
+ * accessed via {@link PythonObject#ref}. This node should be used of you have the
+ * {@link PythonObject} at hand.
+ */
@GenerateUncached
@GenerateInline
@GenerateCached(false)
- public abstract static class NativePtrToPythonWrapperNode extends Node {
-
- public abstract PythonNativeWrapper execute(Node inliningTarget, long ptr, boolean strict);
-
- @Specialization
- static PythonNativeWrapper doGeneric(Node inliningTarget, long pointer, boolean strict,
- @Cached(inline = false) CStructAccess.ReadI32Node readI32Node,
- @Cached InlinedConditionProfile isNullProfile,
- @Cached InlinedConditionProfile isNativeProfile,
- @Cached InlinedExactClassProfile nativeWrapperProfile,
- @Cached InlinedConditionProfile isHandleSpaceProfile) {
- if (isNullProfile.profile(inliningTarget, pointer == 0)) {
- return null;
- }
- PythonContext pythonContext = PythonContext.get(inliningTarget);
- HandleContext nativeContext = pythonContext.nativeContext;
- assert pythonContext.ownsGil();
- if (isHandleSpaceProfile.profile(inliningTarget, HandlePointerConverter.pointsToPyHandleSpace(pointer))) {
- if (HandlePointerConverter.pointsToPyIntHandle(pointer)) {
- throw CompilerDirectives.shouldNotReachHere("not implemented NativePtrToPythonWrapperNode int");
- } else if (HandlePointerConverter.pointsToPyFloatHandle(pointer)) {
- throw CompilerDirectives.shouldNotReachHere("not implemented NativePtrToPythonWrapperNode float");
- }
- int idx = readI32Node.read(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index);
- PythonObjectReference reference = nativeStubLookupGet(nativeContext, pointer, idx);
- PythonNativeWrapper wrapper;
- if (strict && reference == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw CompilerDirectives.shouldNotReachHere("reference was freed: " + Long.toHexString(pointer));
- }
- wrapper = reference == null ? null : reference.get();
- if (strict && wrapper == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw CompilerDirectives.shouldNotReachHere("reference was collected: " + Long.toHexString(pointer));
- }
- return wrapper;
- } else {
- IdReference> lookup = nativeLookupGet(nativeContext, pointer);
- if (isNativeProfile.profile(inliningTarget, lookup != null)) {
- Object ref = lookup.get();
- if (strict && ref == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw CompilerDirectives.shouldNotReachHere("reference was collected: " + Long.toHexString(pointer));
- }
- Object profiled = nativeWrapperProfile.profile(inliningTarget, ref);
- if (profiled instanceof PythonNativeWrapper nativeWrapper) {
- return nativeWrapper;
- }
- }
- return null;
- }
- }
- }
-
- @GenerateUncached
- @GenerateInline(false)
- @ImportStatic(CApiGuards.class)
- public abstract static class ToPythonWrapperNode extends CExtToJavaNode {
+ public abstract static class UpdateStrongRefNode extends Node {
- public static PythonNativeWrapper executeUncached(Object obj, boolean strict) {
- return getUncached().executeWrapper(obj, strict);
+ public final void execute(Node inliningTarget, PythonObject pythonObject, long refCount) {
+ execute(inliningTarget, pythonObject, refCount > MANAGED_REFCNT, false, false);
}
- @Override
- public final Object execute(Object object) {
- return executeWrapper(object, true);
+ public final void execute(Node inliningTarget, PythonObject pythonObject, long refCount, boolean release) {
+ execute(inliningTarget, pythonObject, refCount > MANAGED_REFCNT, release, false);
}
- public abstract PythonNativeWrapper executeWrapper(Object obj, boolean strict);
-
- @Specialization(guards = "!isNativeWrapper(obj)", limit = "3")
- static PythonNativeWrapper doNonWrapper(Object obj, boolean strict,
- @Bind Node inliningTarget,
- @CachedLibrary("obj") InteropLibrary interopLibrary,
- @Cached NativePtrToPythonWrapperNode nativePtrToPythonWrapperNode,
- @Cached InlinedConditionProfile isLongProfile) {
- long pointer;
- if (isLongProfile.profile(inliningTarget, obj instanceof Long)) {
- pointer = (long) obj;
- } else {
- if (!interopLibrary.isPointer(obj)) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw CompilerDirectives.shouldNotReachHere("not a pointer: " + obj);
- }
- try {
- pointer = interopLibrary.asPointer(obj);
- } catch (final UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
- return nativePtrToPythonWrapperNode.execute(inliningTarget, pointer, strict);
- }
+ protected abstract void execute(Node inliningTarget, PythonObject pythonObject, boolean setStrong, boolean release, boolean keepInGcList);
@Specialization
- static PythonNativeWrapper doWrapper(PythonNativeWrapper wrapper, @SuppressWarnings("unused") boolean strict) {
- return wrapper;
- }
-
- @NeverDefault
- public static ToPythonWrapperNode create() {
- return CApiTransitionsFactory.ToPythonWrapperNodeGen.create();
- }
+ static void doGeneric(Node inliningTarget, PythonObject pythonObject, boolean setStrong, boolean release, boolean keepInGcList,
+ @Cached InlinedBranchProfile hasWeakRef,
+ @Cached PyObjectGCTrackNode gcTrackNode,
+ @Cached GCListRemoveNode gcListRemoveNode,
+ @Cached InlinedConditionProfile isGcProfile,
+ @Cached GetPythonObjectClassNode getClassNode,
+ @Cached(inline = false) GetTypeFlagsNode getTypeFlagsNode) {
+ assert CompilerDirectives.isPartialEvaluationConstant(release);
+ assert CompilerDirectives.isPartialEvaluationConstant(keepInGcList);
- public static ToPythonWrapperNode getUncached() {
- return CApiTransitionsFactory.ToPythonWrapperNodeGen.getUncached();
- }
- }
+ boolean isLoggable = LOGGER.isLoggable(Level.FINER);
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WrappedPointerToPythonNode extends CExtToJavaNode {
- @Specialization
- static Object doIt(Object object) {
- if (object instanceof PythonNativeWrapper) {
- return ((PythonNativeWrapper) object).getDelegate();
- } else {
- return object;
+ /*
+ * There are two cases: (1) the pythonObject has a PythonObjectReference, and (2)
+ * doesn't have one. In case of (2), the object was strongly referenced so far and we
+ * may now need to introduce a weak reference.
+ */
+ long taggedPointer = pythonObject.getNativePointer();
+ PythonObjectReference pythonObjectReference;
+ if ((pythonObjectReference = pythonObject.ref) != null) {
+ hasWeakRef.enter(inliningTarget);
+ UpdateHandleTableReferenceNode.handlePythonObjectReference(inliningTarget, pythonObjectReference, taggedPointer, pythonObjectReference.handleTableIndex,
+ setStrong, keepInGcList, isLoggable,
+ gcTrackNode, gcListRemoveNode);
+ Reference.reachabilityFence(pythonObject);
+ } else if (!setStrong) {
+ // 'pythonObject.ref == null' -> reference is strong
+
+ HandleContext handleContext = PythonContext.get(inliningTarget).handleContext;
+ long untaggedPointer = HandlePointerConverter.pointerToStub(taggedPointer);
+ int handleTableIndex = readIntField(untaggedPointer, CFields.GraalPyObject__handle_table_index);
+ assert nativeStubLookupGet(handleContext, taggedPointer, handleTableIndex) == pythonObject;
+
+ UpdateHandleTableReferenceNode.makeDirectReferenceWeak(inliningTarget, handleContext, pythonObject, taggedPointer, handleTableIndex,
+ release, keepInGcList, isLoggable,
+ gcListRemoveNode, isGcProfile, getClassNode, getTypeFlagsNode);
}
}
}
/**
- * Adjusts the native wrapper's reference to be weak (if {@code refCount <= MANAGED_REFCNT}) or
- * to be strong (if {@code refCount > MANAGED_REFCNT}) if there is a reference. This node should
- * be called at appropriate points in the program, e.g., it should be called from native code if
- * the refcount falls below {@link PythonAbstractObjectNativeWrapper#MANAGED_REFCNT}.
+ * Adjusts the handle table reference of a PythonObject to be weak (if
+ * {@code refCount <= MANAGED_REFCNT}) or to be strong (if {@code refCount > MANAGED_REFCNT}).
+ * The handle table reference is identified by the native (tagged) pointer of the managed object
+ * and the handle table index. This node needs to be called every time the reference count of a
+ * native companion is changed. Therefore, immortal objects don't need this operation.
*
- * Additionally, if the reference to a wrapper will be made weak and the wrapper takes part in
- * the Python GC and is currently tracked, it will be removed from the GC list. This is done to
- * reduce the GC list size and avoid repeated upcalls to ensure that a
- * {@link PythonObjectReference} is weak.
+ * Additionally, if the reference to the PythonObject is up to be made weak and the object is a
+ * tracked GC object, it will be removed from the GC list. This is necessary because weak
+ * objects may die anytime but if their native companion is still reachable from the GC list, we
+ * will fail resolving the object for replicating the native references.
*/
@GenerateUncached
@GenerateInline
@GenerateCached(false)
- public abstract static class UpdateStrongRefNode extends Node {
+ public abstract static class UpdateHandleTableReferenceNode extends Node {
- public final void execute(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper, long refCount) {
- execute(inliningTarget, wrapper, refCount > MANAGED_REFCNT, false);
+ public final void execute(Node inliningTarget, HandleContext handleContext, long pointer, int handleTableIndex, long refCount) {
+ execute(inliningTarget, handleContext, pointer, handleTableIndex, refCount > MANAGED_REFCNT, false);
}
/**
@@ -2216,42 +2686,152 @@ public final void execute(Node inliningTarget, PythonAbstractObjectNativeWrapper
* method is when iterating over all objects of a GC list, calling this method on each
* object and in the end, dropping the whole GC list.
*/
- public final void clearStrongRefButKeepInGCList(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper) {
- execute(inliningTarget, wrapper, false, true);
+ public final void clearStrongRefButKeepInGCList(Node inliningTarget, HandleContext handleContext, long pointer, int handleTableIndex) {
+ execute(inliningTarget, handleContext, pointer, handleTableIndex, false, true);
}
- public abstract void execute(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper, boolean setStrong, boolean keepInGcList);
+ @TruffleBoundary
+ public static void clearStrongRefButKeepInGCListUncached(HandleContext handleContext, long pointer, int handleTableIndex) {
+ CApiTransitionsFactory.UpdateHandleTableReferenceNodeGen.getUncached().clearStrongRefButKeepInGCList(null, handleContext, pointer, handleTableIndex);
+ }
+
+ protected abstract void execute(Node inliningTarget, HandleContext handleContext, long pointer, int handleTableIndex, boolean setStrong, boolean keepInGcList);
@Specialization
- static void doGeneric(Node inliningTarget, PythonAbstractObjectNativeWrapper wrapper, boolean setStrong, boolean keepInGcList,
- @Cached InlinedConditionProfile hasRefProfile,
+ static void doGeneric(Node inliningTarget, HandleContext handleContext, long taggedPointer, int handleTableIndex, boolean setStrong, boolean keepInGcList,
+ @Cached InlinedBranchProfile hasWeakRef,
@Cached PyObjectGCTrackNode gcTrackNode,
- @Cached GCListRemoveNode gcListRemoveNode) {
+ @Cached GCListRemoveNode gcListRemoveNode,
+ @Cached InlinedConditionProfile isGcProfile,
+ @Cached GetPythonObjectClassNode getClassNode,
+ @Cached(inline = false) GetTypeFlagsNode getTypeFlagsNode) {
assert CompilerDirectives.isPartialEvaluationConstant(keepInGcList);
- PythonObjectReference ref;
- if (hasRefProfile.profile(inliningTarget, (ref = wrapper.ref) != null)) {
- assert ref.pointer == wrapper.getNativePointer();
- if (setStrong && !ref.isStrongReference()) {
- ref.setStrongReference(wrapper);
- if (ref.gc && PythonLanguage.get(inliningTarget).getEngineOption(PythonOptions.PythonGC)) {
- // gc = AS_GC(op)
- long gc = ref.pointer - CStructs.PyGC_Head.size();
- gcTrackNode.execute(inliningTarget, gc);
- }
- } else if (!setStrong && ref.isStrongReference()) {
- /*
- * As soon as the reference is made weak, we remove it from the GC list because
- * there are ways to iterate a GC list (e.g. 'PyUnstable_GC_VisitObjects') and
- * while doing so, the objects may be accessed. Since weakly referenced objects
- * may die any time, this could lead to dangling pointers being used.
- */
- if (!keepInGcList && ref.gc) {
- gcListRemoveNode.executeOp(inliningTarget, ref.pointer);
- }
- ref.setStrongReference(null);
+ boolean isLoggable = LOGGER.isLoggable(Level.FINER);
+
+ /*
+ * There are two cases: (1) the pythonObject has a PythonObjectReference, and (2)
+ * doesn't have one. In case of (2), the object was strongly referenced so far and we
+ * may now need to introduce a weak reference.
+ */
+ assert HandlePointerConverter.pointsToPyHandleSpace(taggedPointer);
+ assert !HandlePointerConverter.pointsToPyIntHandle(taggedPointer);
+ assert !HandlePointerConverter.pointsToPyFloatHandle(taggedPointer);
+
+ Object ref = nativeStubLookupGet(handleContext, taggedPointer, handleTableIndex);
+ if (ref instanceof PythonObjectReference pythonObjectReference) {
+ hasWeakRef.enter(inliningTarget);
+ handlePythonObjectReference(inliningTarget, pythonObjectReference, taggedPointer, handleTableIndex,
+ setStrong, keepInGcList, isLoggable,
+ gcTrackNode, gcListRemoveNode);
+ } else if (!setStrong) {
+ // no PythonObjectReference in the handle table -> reference is strong
+
+ /*
+ * At this point, it must be a PythonObject because all PythonAbstractObjects that
+ * are not PythonObject (e.g. PEllipsis and such) are immortal and cannot be made
+ * weak.
+ */
+ assert ref instanceof PythonObject;
+ PythonObject pythonObject = (PythonObject) ref;
+ assert pythonObject.ref == null;
+
+ makeDirectReferenceWeak(inliningTarget, handleContext, pythonObject, taggedPointer, handleTableIndex,
+ false, keepInGcList, isLoggable,
+ gcListRemoveNode, isGcProfile, getClassNode, getTypeFlagsNode);
+ }
+ }
+
+ static void handlePythonObjectReference(Node inliningTarget, PythonObjectReference pythonObjectReference, long taggedPointer, int handleTableIndex,
+ boolean setStrong, boolean keepInGcList, boolean isLoggable,
+ PyObjectGCTrackNode gcTrackNode,
+ GCListRemoveNode gcListRemoveNode) {
+ assert pythonObjectReference.handleTableIndex == handleTableIndex;
+ assert pythonObjectReference.pointer == taggedPointer;
+ if (setStrong && !pythonObjectReference.isStrongReference()) {
+ PythonObject pythonObject = pythonObjectReference.get();
+ if (pythonObject == null) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ throw CompilerDirectives.shouldNotReachHere("reference was collected: 0x" + Long.toHexString(taggedPointer));
+ }
+ if (isLoggable) {
+ logWeakToStrong(taggedPointer, handleTableIndex, pythonObject);
+ }
+ pythonObjectReference.setStrongReference(pythonObject);
+ if (pythonObjectReference.gc) {
+ gcTrackNode.executeOp(inliningTarget, taggedPointer);
+ }
+ } else if (!setStrong && pythonObjectReference.isStrongReference()) {
+ if (isLoggable) {
+ logStrongToWeak(taggedPointer, handleTableIndex, pythonObjectReference.strongReference);
+ }
+ if (!keepInGcList && pythonObjectReference.gc) {
+ gcListRemoveNode.executeOp(inliningTarget, taggedPointer);
+ }
+ pythonObjectReference.setStrongReference(null);
+ }
+ }
+
+ static void makeDirectReferenceWeak(Node inliningTarget, HandleContext handleContext, PythonObject pythonObject, long taggedPointer, int handleTableIndex,
+ boolean release, boolean keepInGcList, boolean isLoggable,
+ GCListRemoveNode gcListRemoveNode,
+ InlinedConditionProfile isGcProfile,
+ GetPythonObjectClassNode getClassNode,
+ GetTypeFlagsNode getTypeFlagsNode) {
+ long untaggedPointer = HandlePointerConverter.pointerToStub(taggedPointer);
+
+ if (isLoggable) {
+ logStrongToWeak(taggedPointer, handleTableIndex, pythonObject);
+ }
+
+ Object type = getClassNode.execute(inliningTarget, pythonObject);
+ boolean gc = (getTypeFlagsNode.execute(type) & TypeFlags.HAVE_GC) != 0;
+
+ /*
+ * At this point, we would commonly expect that 'pythonObject.getRefCount() ==
+ * MANAGED_REFCNT'. However, in order to break reference cycles with managed objects, we
+ * make references weak even if that is not the case. So, for all non-gc objects, we
+ * strongly expect MANAGED_REFCNT.
+ */
+ assert gc || pythonObject.getRefCount() == MANAGED_REFCNT;
+
+ if (release) {
+ // clear all links between the managed and the native companion object
+ writeIntField(untaggedPointer, CFields.GraalPyObject__handle_table_index, 0);
+ pythonObject.clearNativePointer();
+ Object removed = CApiTransitions.nativeStubLookupRemove(handleContext, handleTableIndex);
+ assert pythonObject == removed;
+ freeNativeStub(taggedPointer, isGcProfile.profile(inliningTarget, gc));
+ } else {
+ /*
+ * The reference should be weak but we may not release the native object stub. We
+ * need to create a PythonObjectReference.
+ */
+ PythonObjectReference pythonObjectReference = PythonObjectReference.createStub(handleContext, pythonObject, false, taggedPointer, handleTableIndex, gc);
+ nativeStubLookupReplaceByWeak(handleContext, handleTableIndex, pythonObjectReference, taggedPointer);
+
+ /*
+ * As soon as the reference is made weak, we remove it from the GC list because
+ * there are ways to iterate a GC list (e.g. 'PyUnstable_GC_VisitObjects') and while
+ * doing so, the objects may be accessed. Since weakly referenced objects may die
+ * any time, this could lead to dangling pointers being used.
+ */
+ if (!keepInGcList && gc) {
+ gcListRemoveNode.executeOp(inliningTarget, pythonObject.getNativePointer());
}
}
}
+
+ @TruffleBoundary
+ private static void logStrongToWeak(long pointer, int handleTableIndex, PythonObject referent) {
+ LOGGER.finer(String.format("reference 0x%x at index %d (object: %s): strong -> weak",
+ pointer, handleTableIndex, referent));
+ }
+
+ @TruffleBoundary
+ private static void logWeakToStrong(long pointer, int handleTableIndex, PythonObject referent) {
+ LOGGER.finer(String.format("reference 0x%x at index %d (object: %s): weak -> strong",
+ pointer, handleTableIndex, referent));
+ }
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/GetNativeWrapperNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/GetNativeWrapperNode.java
deleted file mode 100644
index 7d8a9d5a00..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/GetNativeWrapperNode.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (c) 2022, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.capi.transitions;
-
-import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.Python3Core;
-import com.oracle.graal.python.builtins.PythonBuiltinClassType;
-import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CApiGuards;
-import com.oracle.graal.python.builtins.objects.cext.capi.PrimitiveNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonClassNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.TruffleObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
-import com.oracle.graal.python.builtins.objects.floats.PFloat;
-import com.oracle.graal.python.builtins.objects.ints.PInt;
-import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
-import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes;
-import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode;
-import com.oracle.graal.python.nodes.PGuards;
-import com.oracle.graal.python.nodes.PNodeWithContext;
-import com.oracle.graal.python.nodes.object.IsForeignObjectNode;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.graal.python.runtime.object.PFactory;
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
-import com.oracle.truffle.api.dsl.Cached.Shared;
-import com.oracle.truffle.api.dsl.GenerateUncached;
-import com.oracle.truffle.api.dsl.ImportStatic;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.profiles.InlinedConditionProfile;
-import com.oracle.truffle.api.strings.TruffleString;
-
-@GenerateUncached
-@SuppressWarnings("truffle-inlining")
-@ImportStatic({PGuards.class, CApiGuards.class})
-public abstract class GetNativeWrapperNode extends PNodeWithContext {
-
- public static Object executeUncached(Object value) {
- return GetNativeWrapperNodeGen.getUncached().execute(value);
- }
-
- public abstract Object execute(Object value);
-
- @Specialization
- static PythonAbstractObjectNativeWrapper doString(TruffleString str,
- @Bind Node inliningTarget,
- @Bind PythonLanguage language,
- @Exclusive @Cached InlinedConditionProfile noWrapperProfile) {
- return PythonObjectNativeWrapper.wrap(PFactory.createString(language, str), inliningTarget, noWrapperProfile);
- }
-
- @Specialization
- static PythonAbstractObjectNativeWrapper doBoolean(boolean b,
- @Bind Node inliningTarget,
- @Exclusive @Cached InlinedConditionProfile profile) {
- Python3Core core = PythonContext.get(inliningTarget);
- PInt boxed = b ? core.getTrue() : core.getFalse();
- PythonAbstractObjectNativeWrapper nativeWrapper = boxed.getNativeWrapper();
- if (profile.profile(inliningTarget, nativeWrapper == null)) {
- CompilerDirectives.transferToInterpreter();
- nativeWrapper = PrimitiveNativeWrapper.createBool(b);
- boxed.setNativeWrapper(nativeWrapper);
- }
- return nativeWrapper;
- }
-
- @Specialization
- static Object doInt(int i) {
- return HandlePointerConverter.intToPointer(i);
- }
-
- @Specialization
- static Object doLong(long l) {
- if (PInt.fitsInInt(l)) {
- return HandlePointerConverter.intToPointer((int) l);
- }
- return PrimitiveNativeWrapper.createLong(l);
- }
-
- @Specialization
- static Object doDouble(double d) {
- if (PFloat.fitsInFloat(d)) {
- return HandlePointerConverter.floatToPointer((float) d);
- }
- return PrimitiveNativeWrapper.createDouble(d);
- }
-
- @Specialization(guards = "isSpecialSingleton(object)")
- static PythonNativeWrapper doSingleton(PythonAbstractObject object,
- @Bind Node inliningTarget) {
- PythonContext context = PythonContext.get(inliningTarget);
- PythonAbstractObjectNativeWrapper nativeWrapper = context.getCApiContext().getSingletonNativeWrapper(object);
- assert nativeWrapper != null;
- return nativeWrapper;
- }
-
- @Specialization
- static PythonNativeWrapper doPythonClassUncached(PythonManagedClass object,
- @Bind Node inliningTarget,
- @Cached TypeNodes.GetTpNameNode getTpNameNode,
- @Shared @Cached TruffleString.SwitchEncodingNode switchEncoding) {
- return PythonClassNativeWrapper.wrap(object, getTpNameNode.execute(inliningTarget, object), switchEncoding);
- }
-
- @Specialization
- static PythonNativeWrapper doPythonTypeUncached(PythonBuiltinClassType object,
- @Bind Node inliningTarget,
- @Shared @Cached TruffleString.SwitchEncodingNode switchEncoding) {
- PythonBuiltinClass type = PythonContext.get(inliningTarget).lookupType(object);
- return PythonClassNativeWrapper.wrap(type, type.getName(), switchEncoding);
- }
-
- @Specialization(guards = {"!isClass(inliningTarget, object, isTypeNode)", "!isNativeObject(object)", "!isSpecialSingleton(object)"}, limit = "1")
- static PythonNativeWrapper runAbstractObject(PythonAbstractObject object,
- @Bind Node inliningTarget,
- @Exclusive @Cached InlinedConditionProfile noWrapperProfile,
- @SuppressWarnings("unused") @Cached IsTypeNode isTypeNode) {
- assert object != PNone.NO_VALUE;
- return PythonObjectNativeWrapper.wrap(object, inliningTarget, noWrapperProfile);
- }
-
- @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, object)", "!isNativeWrapper(object)", "!isNativeNull(object)"}, limit = "1")
- static PythonNativeWrapper doForeignObject(Object object,
- @SuppressWarnings("unused") @Bind Node inliningTarget,
- @SuppressWarnings("unused") @Cached IsForeignObjectNode isForeignObjectNode) {
- assert !CApiTransitions.isBackendPointerObject(object);
- assert !(object instanceof String);
- return TruffleObjectNativeWrapper.wrap(object);
- }
-
- protected static boolean isNaN(double d) {
- return Double.isNaN(d);
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ToNativeTypeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ToNativeTypeNode.java
new file mode 100644
index 0000000000..055d00fe0f
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ToNativeTypeNode.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2018, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.builtins.objects.cext.capi.transitions;
+
+import static com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.lookupNativeI64MemberInMRO;
+import static com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.lookupNativeMemberInMRO;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_refcnt;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_type;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_alloc;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_as_buffer;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_clear;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_dealloc;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_del;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_free;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_is_gc;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_traverse;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_vectorcall_offset;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_weaklistoffset;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writePtrField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+
+import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
+import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
+import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
+import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
+import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
+import com.oracle.graal.python.builtins.objects.common.HashingStorage;
+import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageAddAllToOther;
+import com.oracle.graal.python.builtins.objects.dict.PDict;
+import com.oracle.graal.python.builtins.objects.object.PythonObject;
+import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
+import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
+import com.oracle.graal.python.builtins.objects.type.TpSlots;
+import com.oracle.graal.python.builtins.objects.type.TpSlots.GetTpSlotsNode;
+import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotMeta;
+import com.oracle.graal.python.builtins.objects.type.TypeFlags;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBaseClassNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBaseClassesNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBasicSizeNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetDictOffsetNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetItemSizeNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetMroStorageNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetSubclassesNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetTypeFlagsNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodes.SetTypeFlagsNode;
+import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.GetTypeFlagsNodeGen;
+import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.SetBasicSizeNodeGen;
+import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.SetItemSizeNodeGen;
+import com.oracle.graal.python.nodes.HiddenAttr;
+import com.oracle.graal.python.nodes.SpecialAttributeNames;
+import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile;
+import com.oracle.graal.python.nodes.object.GetClassNode;
+import com.oracle.graal.python.nodes.object.GetOrCreateDictNode;
+import com.oracle.graal.python.nodes.util.CannotCastException;
+import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
+import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.object.PFactory;
+import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleString.Encoding;
+import com.oracle.truffle.api.strings.TruffleString.SwitchEncodingNode;
+
+public abstract class ToNativeTypeNode {
+
+ private static long allocatePyAsyncMethods(TpSlots slots) {
+ long mem = CStructAccess.allocate(CStructs.PyAsyncMethods);
+ writeGroupSlots(CFields.PyTypeObject__tp_as_async, slots, mem);
+ return mem;
+ }
+
+ private static void writeGroupSlots(CFields groupField, TpSlots slots, long groupPointer) {
+ for (TpSlotMeta def : TpSlotMeta.VALUES) {
+ if (def.getNativeGroupOrField() == groupField) {
+ CStructAccess.writePtrField(groupPointer, def.getNativeField(), def.getNativeValue(slots, NULLPTR));
+ }
+ }
+ }
+
+ private static long allocatePyMappingMethods(TpSlots slots) {
+ long mem = CStructAccess.allocate(CStructs.PyMappingMethods);
+ writeGroupSlots(CFields.PyTypeObject__tp_as_mapping, slots, mem);
+ return mem;
+ }
+
+ private static long allocatePyNumberMethods(TpSlots slots) {
+ long mem = CStructAccess.allocate(CStructs.PyNumberMethods);
+ writeGroupSlots(CFields.PyTypeObject__tp_as_number, slots, mem);
+ return mem;
+ }
+
+ private static long allocatePySequenceMethods(TpSlots slots) {
+ long mem = CStructAccess.allocate(CStructs.PyNumberMethods);
+ writeGroupSlots(CFields.PyTypeObject__tp_as_sequence, slots, mem);
+ return mem;
+ }
+
+ private static long lookup(PythonManagedClass clazz, CFields member, HiddenAttr hiddenName) {
+ return lookupNativeMemberInMRO(clazz, member, hiddenName);
+ }
+
+ private static long lookupSize(PythonManagedClass clazz, CFields member, HiddenAttr hiddenName) {
+ return lookupNativeI64MemberInMRO(clazz, member, hiddenName);
+ }
+
+ public static void initNative(PythonManagedClass clazz, long pointer, int typeLookupTableIdx) {
+ initializeType(clazz, pointer, false, typeLookupTableIdx);
+ }
+
+ static void initializeType(PythonManagedClass clazz, long mem, boolean heaptype, int typeLookupTableIdx) {
+ CompilerAsserts.neverPartOfCompilation();
+
+ TpSlots slots = GetTpSlotsNode.executeUncached(clazz);
+ boolean isType = IsBuiltinClassExactProfile.profileClassSlowPath(clazz, PythonBuiltinClassType.PythonClass);
+
+ PythonToNativeNode toNative = PythonToNativeNode.getUncached();
+ PythonToNativeNewRefNode toNativeNewRef = PythonToNativeNewRefNode.getUncached();
+ GetTypeFlagsNode getTypeFlagsNode = GetTypeFlagsNodeGen.getUncached();
+
+ PythonContext ctx = PythonContext.get(null);
+ PythonLanguage language = ctx.getLanguage();
+
+ // make this object immortal
+ writeLongField(mem, PyObject__ob_refcnt, PythonObject.IMMORTAL_REFCNT);
+ if (isType) {
+ // self-reference
+ writePtrField(mem, PyObject__ob_type, mem);
+ } else {
+ PythonAbstractObject promotedType = EnsurePythonObjectNode.executeUncached(ctx, GetClassNode.executeUncached(clazz));
+ writePtrField(mem, PyObject__ob_type, toNative.executeLong(promotedType));
+ }
+
+ long flags = getTypeFlagsNode.execute(clazz);
+ /*
+ * Our datetime classes are declared as static types in C, but are implemented as
+ * pure-python heaptypes. Make them into static types on the C-side.
+ */
+ if (!heaptype) {
+ flags &= ~TypeFlags.HEAPTYPE;
+ }
+
+ Object base = GetBaseClassNode.executeUncached(clazz);
+ if (base == null) {
+ base = PNone.NO_VALUE;
+ }
+
+ writeLongField(mem, CFields.PyVarObject__ob_size, 0L);
+
+ TruffleString nameUtf8 = SwitchEncodingNode.getUncached().execute(clazz.getName(), Encoding.UTF_8);
+ // TODO(fa): the allocated 'char *' will be free'd at context finalization. It should be
+ // free'd if the type is free'd.
+ TruffleString nativeUncached = nameUtf8.asNativeUncached(ctx::allocateContextMemory, Encoding.UTF_8, false, true);
+ Object internalNativePointerUncached = nativeUncached.getInternalNativePointerUncached(Encoding.UTF_8);
+ long namePointer = PythonUtils.coerceToLong(internalNativePointerUncached, InteropLibrary.getUncached());
+
+ writePtrField(mem, CFields.PyTypeObject__tp_name, namePointer);
+ writeLongField(mem, CFields.PyTypeObject__tp_basicsize, GetBasicSizeNode.executeUncached(clazz));
+ writeLongField(mem, CFields.PyTypeObject__tp_itemsize, GetItemSizeNode.executeUncached(clazz));
+ // writeStructMemberLong(mem, CFields.PyTypeObject__tp_weaklistoffset,
+ // GetWeakListOffsetNode.executeUncached(clazz));
+ /*
+ * TODO msimacek: this should use GetWeakListOffsetNode as in the commented out code above.
+ * Unfortunately, it causes memory corruption in several libraries
+ */
+ long weaklistoffset;
+ if (clazz instanceof PythonBuiltinClass builtin) {
+ weaklistoffset = builtin.getType().getWeaklistoffset();
+ } else {
+ weaklistoffset = lookupNativeI64MemberInMRO(clazz, PyTypeObject__tp_weaklistoffset, SpecialAttributeNames.T___WEAKLISTOFFSET__);
+ }
+ long asAsync = slots.has_as_async() ? allocatePyAsyncMethods(slots) : NULLPTR;
+ long asNumber = slots.has_as_number() ? allocatePyNumberMethods(slots) : NULLPTR;
+ long asSequence = slots.has_as_sequence() ? allocatePySequenceMethods(slots) : NULLPTR;
+ long asMapping = slots.has_as_mapping() ? allocatePyMappingMethods(slots) : NULLPTR;
+ long asBuffer = lookup(clazz, PyTypeObject__tp_as_buffer, HiddenAttr.AS_BUFFER);
+ writeLongField(mem, CFields.PyTypeObject__tp_weaklistoffset, weaklistoffset);
+ writePtrField(mem, CFields.PyTypeObject__tp_dealloc, lookup(clazz, PyTypeObject__tp_dealloc, HiddenAttr.DEALLOC));
+ writeLongField(mem, CFields.PyTypeObject__tp_vectorcall_offset, lookupSize(clazz, PyTypeObject__tp_vectorcall_offset, HiddenAttr.VECTORCALL_OFFSET));
+ writePtrField(mem, CFields.PyTypeObject__tp_getattr, NULLPTR);
+ writePtrField(mem, CFields.PyTypeObject__tp_as_async, asAsync);
+ writePtrField(mem, CFields.PyTypeObject__tp_as_number, asNumber);
+ writePtrField(mem, CFields.PyTypeObject__tp_as_sequence, asSequence);
+ writePtrField(mem, CFields.PyTypeObject__tp_as_mapping, asMapping);
+ writePtrField(mem, CFields.PyTypeObject__tp_as_buffer, asBuffer);
+ writeLongField(mem, CFields.PyTypeObject__tp_flags, flags);
+
+ // return a C string wrapper that really allocates 'char*' on TO_NATIVE
+ Object docObj = clazz.getAttribute(SpecialAttributeNames.T___DOC__);
+ long docPtr;
+ try {
+ docPtr = ctx.stringToNativeUtf8Bytes(CastToTruffleStringNode.executeUncached(docObj), true);
+ } catch (CannotCastException e) {
+ // if not directly a string, give up (we don't call descriptors here)
+ docPtr = NULLPTR;
+ }
+ writePtrField(mem, CFields.PyTypeObject__tp_doc, docPtr);
+
+ long tpTraverse = NULLPTR;
+ long tpIsGc = NULLPTR;
+ if ((flags & TypeFlags.HAVE_GC) != 0) {
+ tpTraverse = lookup(clazz, PyTypeObject__tp_traverse, HiddenAttr.TRAVERSE);
+ tpIsGc = lookup(clazz, PyTypeObject__tp_is_gc, HiddenAttr.IS_GC);
+ }
+ writePtrField(mem, CFields.PyTypeObject__tp_traverse, tpTraverse);
+ writePtrField(mem, CFields.PyTypeObject__tp_is_gc, tpIsGc);
+
+ writePtrField(mem, CFields.PyTypeObject__tp_methods, NULLPTR);
+ writePtrField(mem, CFields.PyTypeObject__tp_members, NULLPTR);
+ writePtrField(mem, CFields.PyTypeObject__tp_getset, NULLPTR);
+ if (!isType) {
+ // "object" base needs to be initialized explicitly in capi.c
+ PythonAbstractObject promotedBase = EnsurePythonObjectNode.executeUncached(ctx, base);
+ writePtrField(mem, CFields.PyTypeObject__tp_base, toNative.executeLong(promotedBase));
+ }
+
+ // TODO(fa): we could cache the dict instance on the class' native wrapper
+ PDict dict = GetOrCreateDictNode.executeUncached(clazz);
+ HashingStorage dictStorage = dict.getDictStorage();
+ if (!(dictStorage instanceof DynamicObjectStorage)) {
+ HashingStorage storage = new DynamicObjectStorage(clazz);
+ // copy all mappings to the new storage
+ dict.setDictStorage(HashingStorageAddAllToOther.executeUncached(dictStorage, storage));
+ }
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(dict);
+ writePtrField(mem, CFields.PyTypeObject__tp_dict, toNative.executeLong(dict));
+
+ for (TpSlotMeta def : TpSlotMeta.VALUES) {
+ if (!def.hasGroup() && def.hasNativeWrapperFactory()) {
+ writePtrField(mem, def.getNativeGroupOrField(), def.getNativeValue(slots, NULLPTR));
+ }
+ }
+
+ // TODO properly implement 'tp_dictoffset' for builtin classes
+ writeLongField(mem, CFields.PyTypeObject__tp_dictoffset, GetDictOffsetNode.executeUncached(clazz));
+ writePtrField(mem, CFields.PyTypeObject__tp_alloc, lookup(clazz, PyTypeObject__tp_alloc, HiddenAttr.ALLOC));
+ writePtrField(mem, CFields.PyTypeObject__tp_free, lookup(clazz, PyTypeObject__tp_free, HiddenAttr.FREE));
+ writePtrField(mem, CFields.PyTypeObject__tp_clear, lookup(clazz, PyTypeObject__tp_clear, HiddenAttr.CLEAR));
+ if (clazz.basesTuple == null) {
+ clazz.basesTuple = PFactory.createTuple(language, GetBaseClassesNode.executeUncached(clazz));
+ }
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(clazz.basesTuple);
+ writePtrField(mem, CFields.PyTypeObject__tp_bases, toNative.executeLong(clazz.basesTuple));
+ if (clazz.mroStore == null) {
+ clazz.mroStore = PFactory.createTuple(language, GetMroStorageNode.executeUncached(clazz));
+ }
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(clazz.mroStore);
+ writePtrField(mem, CFields.PyTypeObject__tp_mro, toNative.executeLong(clazz.mroStore));
+ writePtrField(mem, CFields.PyTypeObject__tp_cache, NULLPTR);
+ PDict subclasses = GetSubclassesNode.executeUncached(clazz);
+ writePtrField(mem, CFields.PyTypeObject__tp_subclasses, toNativeNewRef.executeLong(subclasses));
+ writePtrField(mem, CFields.PyTypeObject__tp_weaklist, NULLPTR);
+ writePtrField(mem, CFields.PyTypeObject__tp_del, lookup(clazz, PyTypeObject__tp_del, HiddenAttr.DEL));
+
+ /*
+ * We store the type lookup table index in field 'tp_version_tag' because we don't use that
+ * field as CPython does and it's an internal field.
+ */
+ writeIntField(mem, CFields.PyTypeObject__tp_version_tag, typeLookupTableIdx);
+
+ writePtrField(mem, CFields.PyTypeObject__tp_finalize, NULLPTR);
+ writePtrField(mem, CFields.PyTypeObject__tp_vectorcall, NULLPTR);
+
+ if (heaptype) {
+ assert (flags & TypeFlags.HEAPTYPE) != 0;
+ writePtrField(mem, CFields.PyHeapTypeObject__as_async, asAsync);
+ writePtrField(mem, CFields.PyHeapTypeObject__as_number, asNumber);
+ writePtrField(mem, CFields.PyHeapTypeObject__as_mapping, asMapping);
+ writePtrField(mem, CFields.PyHeapTypeObject__as_sequence, asSequence);
+ writePtrField(mem, CFields.PyHeapTypeObject__as_buffer, asBuffer);
+ writePtrField(mem, CFields.PyHeapTypeObject__ht_name, toNativeNewRef.executeLong(clazz.getName()));
+ writePtrField(mem, CFields.PyHeapTypeObject__ht_qualname, toNativeNewRef.executeLong(clazz.getQualName()));
+ writePtrField(mem, CFields.PyHeapTypeObject__ht_module, NULLPTR);
+ Object dunderSlots = clazz.getAttribute(SpecialAttributeNames.T___SLOTS__);
+ writePtrField(mem, CFields.PyHeapTypeObject__ht_slots, dunderSlots != PNone.NO_VALUE ? toNativeNewRef.executeLong(dunderSlots) : NULLPTR);
+ }
+ }
+
+ /**
+ * Creates a wrapper that uses existing native memory as native replacement object.
+ */
+ public static int wrapStaticTypeStructForManagedClass(PythonManagedClass clazz, long pointer) {
+ /*
+ * This *MUST NOT* happen, otherwise we would allocate a fresh native type store and then
+ * the native pointer of the wrapper would not be equal to the corresponding native global
+ * variable. E.g. 'Py_TYPE(PyBaseObjec_Type) != &PyType_Type'.
+ */
+ if (clazz.isNative()) {
+ throw CompilerDirectives.shouldNotReachHere();
+ }
+
+ // some values are retained from the native representation
+ long basicsize = readLongField(pointer, CFields.PyTypeObject__tp_basicsize);
+ if (basicsize != 0) {
+ SetBasicSizeNodeGen.getUncached().execute(null, clazz, basicsize);
+ }
+ long itemsize = readLongField(pointer, CFields.PyTypeObject__tp_itemsize);
+ if (itemsize != 0) {
+ SetItemSizeNodeGen.getUncached().execute(null, clazz, itemsize);
+ }
+ long vectorcall_offset = readLongField(pointer, CFields.PyTypeObject__tp_vectorcall_offset);
+ if (vectorcall_offset != 0) {
+ HiddenAttr.WriteNode.executeUncached(clazz, HiddenAttr.VECTORCALL_OFFSET, vectorcall_offset);
+ }
+ long alloc_fun = readPtrField(pointer, CFields.PyTypeObject__tp_alloc);
+ if (alloc_fun != NULLPTR) {
+ HiddenAttr.WriteLongNode.executeUncached(clazz, HiddenAttr.ALLOC, alloc_fun);
+ }
+ long dealloc_fun = readPtrField(pointer, CFields.PyTypeObject__tp_dealloc);
+ if (dealloc_fun != NULLPTR) {
+ HiddenAttr.WriteLongNode.executeUncached(clazz, HiddenAttr.DEALLOC, dealloc_fun);
+ }
+ long free_fun = readPtrField(pointer, CFields.PyTypeObject__tp_free);
+ if (free_fun != NULLPTR) {
+ HiddenAttr.WriteLongNode.executeUncached(clazz, HiddenAttr.FREE, free_fun);
+ }
+ long traverse_fun = readPtrField(pointer, CFields.PyTypeObject__tp_traverse);
+ if (traverse_fun != NULLPTR) {
+ HiddenAttr.WriteLongNode.executeUncached(clazz, HiddenAttr.TRAVERSE, traverse_fun);
+ }
+ long is_gc_fun = readPtrField(pointer, CFields.PyTypeObject__tp_is_gc);
+ if (is_gc_fun != NULLPTR) {
+ HiddenAttr.WriteLongNode.executeUncached(clazz, HiddenAttr.IS_GC, is_gc_fun);
+ }
+ long clear_fun = readPtrField(pointer, CFields.PyTypeObject__tp_clear);
+ if (clear_fun != NULLPTR) {
+ HiddenAttr.WriteLongNode.executeUncached(clazz, HiddenAttr.CLEAR, clear_fun);
+ }
+ long as_buffer = readPtrField(pointer, CFields.PyTypeObject__tp_as_buffer);
+ if (as_buffer != NULLPTR) {
+ HiddenAttr.WriteLongNode.executeUncached(clazz, HiddenAttr.AS_BUFFER, as_buffer);
+ }
+
+ /*
+ * Initialize type flags: If the native type, we are wrapping, already defines 'tp_flags',
+ * we use it because those must stay consistent with slots. For example, native
+ * tp_new/tp_alloc/tp_dealloc/tp_free functions must be consistent with
+ * 'Py_TPFLAGS_HAVE_GC'.
+ */
+ long flags = readLongField(pointer, CFields.PyTypeObject__tp_flags);
+ if (flags == 0) {
+ flags = GetTypeFlagsNode.executeUncached(clazz) | TypeFlags.READY | TypeFlags.IMMUTABLETYPE;
+ }
+ SetTypeFlagsNode.executeUncached(clazz, flags);
+
+ // TODO(fa): revisit this: static classes are immortal; we don't need a
+ // PythonObjectReference
+ int nativeTypeId = CApiTransitions.createPythonManagedClassReference(clazz, pointer, false);
+ assert clazz.isNative();
+ return nativeTypeId;
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CArrayWrappers.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CArrayWrappers.java
deleted file mode 100644
index 712ce07586..0000000000
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CArrayWrappers.java
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * Copyright (c) 2018, 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
- *
- * Subject to the condition set forth below, permission is hereby granted to any
- * person obtaining a copy of this software, associated documentation and/or
- * data (collectively the "Software"), free of charge and under any and all
- * copyright rights in the Software, and any and all patent rights owned or
- * freely licensable by each licensor hereunder covering either (i) the
- * unmodified Software as contributed to or provided by such licensor, or (ii)
- * the Larger Works (as defined below), to deal in both
- *
- * (a) the Software, and
- *
- * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
- * one is included with the Software each a "Larger Work" to which the Software
- * is contributed by such licensors),
- *
- * without restriction, including without limitation the rights to copy, create
- * derivative works of, display, perform, and distribute the Software and make,
- * use, sell, offer for sale, import, export, have made, and have sold the
- * Software and the Larger Work(s), and to sublicense the foregoing rights on
- * either these or other terms.
- *
- * This license is subject to the following condition:
- *
- * The above copyright notice and either this complete permission notice or at a
- * minimum a reference to the UPL must be included in all copies or substantial
- * portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package com.oracle.graal.python.builtins.objects.cext.common;
-
-import static com.oracle.graal.python.util.PythonUtils.byteArraySupport;
-
-import java.nio.ByteOrder;
-
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonStructNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonObjectNativeWrapper;
-import com.oracle.graal.python.builtins.objects.ints.PInt;
-import com.oracle.graal.python.nodes.ErrorMessages;
-import com.oracle.graal.python.runtime.GilNode;
-import com.oracle.graal.python.runtime.PythonContext;
-import com.oracle.graal.python.util.OverflowException;
-import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Bind;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.InvalidArrayIndexException;
-import com.oracle.truffle.api.interop.InvalidBufferOffsetException;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.library.CachedLibrary;
-import com.oracle.truffle.api.library.ExportLibrary;
-import com.oracle.truffle.api.library.ExportMessage;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.strings.TruffleString;
-import com.oracle.truffle.api.strings.TruffleString.Encoding;
-
-import sun.misc.Unsafe;
-
-/**
- * Native wrappers for managed objects such that they can be used as a C array by native code. The
- * major difference to other native wrappers is that they are copied to native memory if it receives
- * {@code toNative}. This is primarily necessary for C primitive array like {@code char* arr}. The
- * {@code toNative} transformation directly uses {@code Unsafe} to save unnecessary round trips
- * between Python and Sulong.
- */
-public abstract class CArrayWrappers {
- public static final Unsafe UNSAFE = PythonUtils.initUnsafe();
- private static final long SIZEOF_INT64 = 8;
-
- /**
- * Uses {@code Unsafe} to allocate enough off-heap memory for the provided {@code byte[]} and
- * the copies the contents to the native memory.
- */
- @TruffleBoundary
- public static long byteArrayToNativeInt8(byte[] data, boolean writeNullTerminator) {
- int size = data.length * Byte.BYTES;
- long ptr = UNSAFE.allocateMemory(size + (writeNullTerminator ? Byte.BYTES : 0));
- UNSAFE.copyMemory(data, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, ptr, size);
- if (writeNullTerminator) {
- UNSAFE.putByte(ptr + size, (byte) 0);
- }
- return ptr;
- }
-
- /**
- * Uses {@code Unsafe} to allocate enough off-heap memory for the provided {@code int[]} and the
- * copies the contents to the native memory.
- */
- @TruffleBoundary
- public static long intArrayToNativeInt32(int[] data) {
- int size = data.length * Integer.BYTES;
- long ptr = UNSAFE.allocateMemory(size);
- UNSAFE.copyMemory(data, Unsafe.ARRAY_INT_BASE_OFFSET, null, ptr, size);
- return ptr;
- }
-
- /**
- * Copies a Java {@code int[]} to a native {@code int64_t *}. For this, the native memory is
- * allocated off-heap using {@code Unsafe}.
- */
- public static long intArrayToNativeInt64(int[] data) {
- long size = data.length * SIZEOF_INT64;
- long ptr = allocateBoundary(size);
- // we need to copy element-wise because the int needs to be converted to a long
- for (int i = 0; i < data.length; i++) {
- UNSAFE.putLong(ptr + i * SIZEOF_INT64, data[i]);
- }
- return ptr;
- }
-
- /**
- * Encodes the provided TruffleString as UTF-8 bytes and copies the bytes (and an additional NUL
- * char) to a freshly allocated off-heap {@code int8*} (using {@code Unsafe}).
- */
- public static long stringToNativeUtf8Bytes(TruffleString string, TruffleString.SwitchEncodingNode switchEncodingNode, TruffleString.CopyToByteArrayNode copyToByteArrayNode) {
- // TODO GR-37216: use CopyToNative
- TruffleString utf8 = switchEncodingNode.execute(string, Encoding.UTF_8);
- byte[] data = new byte[utf8.byteLength(Encoding.UTF_8)];
- copyToByteArrayNode.execute(utf8, 0, data, 0, data.length, Encoding.UTF_8);
- return byteArrayToNativeInt8(data, true);
- }
-
- @TruffleBoundary
- private static long allocateBoundary(long size) {
- return UNSAFE.allocateMemory(size);
- }
-
- @TruffleBoundary
- private static void freeBoundary(long address) {
- UNSAFE.freeMemory(address);
- }
-
- @ExportLibrary(InteropLibrary.class)
- public abstract static class CArrayWrapper extends PythonStructNativeWrapper {
-
- public CArrayWrapper(Object delegate) {
- super(delegate);
- }
-
- @ExportMessage
- boolean isPointer() {
- return isNative();
- }
-
- @ExportMessage
- long asPointer() {
- return getNativePointer();
- }
-
- public void free() {
- if (isNative()) {
- freeBoundary(getNativePointer());
- }
- }
- }
-
- /**
- * Unlike a {@link PythonObjectNativeWrapper} object that wraps a Python unicode object, this
- * wrapper let's a TruffleString look like a {@code char*}.
- */
- @ExportLibrary(InteropLibrary.class)
- public static final class CStringWrapper extends CArrayWrapper {
- private TruffleString.Encoding encoding;
-
- public CStringWrapper(TruffleString delegate, TruffleString.Encoding encoding) {
- super(delegate);
- this.encoding = encoding;
- assert delegate.isValidUncached(encoding);
- }
-
- public TruffleString getString() {
- return (TruffleString) getDelegate();
- }
-
- @ExportMessage
- long getArraySize() {
- return getString().byteLength(encoding) + 1;
- }
-
- @ExportMessage
- @SuppressWarnings("static-method")
- boolean hasArrayElements() {
- return true;
- }
-
- @ExportMessage
- byte readArrayElement(long index,
- @CachedLibrary("this") InteropLibrary thisLib) throws InvalidArrayIndexException, UnsupportedMessageException {
- try {
- return thisLib.readBufferByte(this, index);
- } catch (InvalidBufferOffsetException e) {
- throw InvalidArrayIndexException.create(index);
- }
- }
-
- @ExportMessage
- boolean isArrayElementReadable(long index) {
- return 0 <= index && index <= getString().byteLength(encoding);
- }
-
- @ExportMessage
- void toNative(
- @Cached TruffleString.SwitchEncodingNode switchEncodingNode,
- @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) {
- if (!PythonContext.get(switchEncodingNode).isNativeAccessAllowed()) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw new RuntimeException(ErrorMessages.NATIVE_ACCESS_NOT_ALLOWED.toJavaStringUncached());
- }
- if (!isNative()) {
- setNativePointer(stringToNativeUtf8Bytes(getString(), switchEncodingNode, copyToByteArrayNode));
- }
- }
-
- @ExportMessage
- @SuppressWarnings("static-method")
- boolean hasBufferElements() {
- return true;
- }
-
- @ExportMessage
- long getBufferSize() {
- return getString().byteLength(encoding) + 1;
- }
-
- @ExportMessage
- byte readBufferByte(long byteOffset,
- @Cached TruffleString.ReadByteNode readByteNode) throws InvalidBufferOffsetException {
- TruffleString s = getString();
- int len = s.byteLength(encoding);
- if (byteOffset >= 0 && byteOffset < len) {
- return (byte) readByteNode.execute(s, (int) byteOffset, encoding);
- } else if (byteOffset == len) {
- return 0;
- } else {
- throw InvalidBufferOffsetException.create(byteOffset, len);
- }
- }
-
- @ExportMessage
- short readBufferShort(ByteOrder byteOrder, long byteOffset,
- @CachedLibrary("this") InteropLibrary thisLib) throws UnsupportedMessageException, InvalidBufferOffsetException {
- byte b1 = thisLib.readBufferByte(this, byteOffset);
- byte b2 = thisLib.readBufferByte(this, byteOffset + 1);
- if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
- return (short) (((b2 & 0xFF) << 8) | (b1 & 0xFF));
- } else {
- return (short) (((b1 & 0xFF) << 8) | (b2 & 0xFF));
- }
- }
-
- @ExportMessage
- int readBufferInt(ByteOrder byteOrder, long byteOffset,
- @CachedLibrary("this") InteropLibrary thisLib) throws UnsupportedMessageException, InvalidBufferOffsetException {
- byte b1 = thisLib.readBufferByte(this, byteOffset);
- byte b2 = thisLib.readBufferByte(this, byteOffset + 1);
- byte b3 = thisLib.readBufferByte(this, byteOffset + 2);
- byte b4 = thisLib.readBufferByte(this, byteOffset + 3);
- if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
- return ((b4 & 0xFF) << 8 * 3) | ((b3 & 0xFF) << 8 * 2) | ((b2 & 0xFF) << 8) | ((b1 & 0xFF));
- } else {
- return ((b1 & 0xFF) << 8 * 3) | ((b2 & 0xFF) << 8 * 2) | ((b3 & 0xFF) << 8) | ((b4 & 0xFF));
- }
- }
-
- @ExportMessage
- long readBufferLong(ByteOrder byteOrder, long byteOffset,
- @CachedLibrary("this") InteropLibrary thisLib) throws UnsupportedMessageException, InvalidBufferOffsetException {
- byte b1 = thisLib.readBufferByte(this, byteOffset);
- byte b2 = thisLib.readBufferByte(this, byteOffset + 1);
- byte b3 = thisLib.readBufferByte(this, byteOffset + 2);
- byte b4 = thisLib.readBufferByte(this, byteOffset + 3);
- byte b5 = thisLib.readBufferByte(this, byteOffset + 4);
- byte b6 = thisLib.readBufferByte(this, byteOffset + 5);
- byte b7 = thisLib.readBufferByte(this, byteOffset + 6);
- byte b8 = thisLib.readBufferByte(this, byteOffset + 7);
- if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
- return ((b8 & 0xFFL) << (8 * 7)) | ((b7 & 0xFFL) << (8 * 6)) | ((b6 & 0xFFL) << (8 * 5)) | ((b5 & 0xFFL) << (8 * 4)) |
- ((b4 & 0xFFL) << (8 * 3)) | ((b3 & 0xFFL) << (8 * 2)) | ((b2 & 0xFFL) << 8) | ((b1 & 0xFFL));
- } else {
- return ((b1 & 0xFFL) << (8 * 7)) | ((b2 & 0xFFL) << (8 * 6)) | ((b3 & 0xFFL) << (8 * 5)) | ((b4 & 0xFFL) << (8 * 4)) |
- ((b5 & 0xFFL) << (8 * 3)) | ((b6 & 0xFFL) << (8 * 2)) | ((b7 & 0xFFL) << 8) | ((b8 & 0xFFL));
- }
- }
-
- @ExportMessage
- float readBufferFloat(ByteOrder byteOrder, long byteOffset,
- @CachedLibrary("this") InteropLibrary thisLib) throws UnsupportedMessageException, InvalidBufferOffsetException {
- return Float.intBitsToFloat(thisLib.readBufferInt(this, byteOrder, byteOffset));
- }
-
- @ExportMessage
- double readBufferDouble(ByteOrder byteOrder, long byteOffset,
- @CachedLibrary("this") InteropLibrary thisLib) throws UnsupportedMessageException, InvalidBufferOffsetException {
- return Double.longBitsToDouble(thisLib.readBufferLong(this, byteOrder, byteOffset));
- }
-
- @ExportMessage
- void readBuffer(long byteOffset, byte[] destination, int destinationOffset, int length,
- @CachedLibrary("this") InteropLibrary thisLib) throws UnsupportedMessageException, InvalidBufferOffsetException {
- if (length < 0) {
- throw InvalidBufferOffsetException.create(byteOffset, length);
- }
- for (int i = 0; i < length; i++) {
- destination[destinationOffset + i] = thisLib.readBufferByte(this, byteOffset + i);
- }
- }
- }
-
- /**
- * A native wrapper for arbitrary byte arrays (i.e. the store of a Python Bytes object) to be
- * used like a {@code char*} pointer.
- */
- @ExportLibrary(InteropLibrary.class)
- @SuppressWarnings("truffle-abstract-export")
- public static final class CByteArrayWrapper extends CArrayWrapper {
-
- public CByteArrayWrapper(byte[] delegate) {
- super(delegate);
- }
-
- public byte[] getByteArray() {
- return ((byte[]) getDelegate());
- }
-
- @ExportMessage
- @SuppressWarnings("static-method")
- boolean hasBufferElements() {
- return true;
- }
-
- @ExportMessage
- @ExportMessage(name = "getArraySize")
- long getBufferSize() {
- return getByteArray().length + 1;
- }
-
- @ExportMessage
- byte readBufferByte(long byteOffset) throws InvalidBufferOffsetException {
- byte[] bytes = getByteArray();
- /*
- * FIXME we only allow reading the NULL byte when reading by bytes, we should also allow
- * that when reading ints etc.
- */
- if (byteOffset == bytes.length) {
- return 0;
- }
- try {
- return bytes[(int) byteOffset];
- } catch (ArrayIndexOutOfBoundsException e) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw InvalidBufferOffsetException.create(byteOffset, bytes.length);
- }
- }
-
- @ExportMessage
- short readBufferShort(ByteOrder order, long byteOffset) throws InvalidBufferOffsetException {
- try {
- return byteArraySupport(order).getShort(getByteArray(), byteOffset);
- } catch (IndexOutOfBoundsException e) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw InvalidBufferOffsetException.create(byteOffset, getByteArray().length);
- }
- }
-
- @ExportMessage
- int readBufferInt(ByteOrder order, long byteOffset) throws InvalidBufferOffsetException {
- try {
- return byteArraySupport(order).getInt(getByteArray(), byteOffset);
- } catch (IndexOutOfBoundsException e) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw InvalidBufferOffsetException.create(byteOffset, getByteArray().length);
- }
- }
-
- @ExportMessage
- long readBufferLong(ByteOrder order, long byteOffset) throws InvalidBufferOffsetException {
- try {
- return byteArraySupport(order).getLong(getByteArray(), byteOffset);
- } catch (IndexOutOfBoundsException e) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw InvalidBufferOffsetException.create(byteOffset, getByteArray().length);
- }
- }
-
- @ExportMessage
- float readBufferFloat(ByteOrder order, long byteOffset) throws InvalidBufferOffsetException {
- return Float.intBitsToFloat(readBufferInt(order, byteOffset));
- }
-
- @ExportMessage
- double readBufferDouble(ByteOrder order, long byteOffset) throws InvalidBufferOffsetException {
- return Double.longBitsToDouble(readBufferLong(order, byteOffset));
- }
-
- @ExportMessage
- @SuppressWarnings("static-method")
- boolean hasArrayElements() {
- return true;
- }
-
- @ExportMessage
- Object readArrayElement(long index,
- @Exclusive @Cached GilNode gil) throws InvalidArrayIndexException {
- boolean mustRelease = gil.acquire();
- try {
- try {
- int idx = PInt.intValueExact(index);
- byte[] arr = getByteArray();
- if (idx >= 0 && idx < arr.length) {
- return arr[idx];
- } else if (idx == arr.length) {
- return (byte) 0;
- }
- } catch (OverflowException e) {
- // fall through
- }
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw InvalidArrayIndexException.create(index);
- } finally {
- gil.release(mustRelease);
- }
- }
-
- @ExportMessage
- boolean isArrayElementReadable(long index) {
- return 0 <= index && index < getBufferSize();
- }
-
- @ExportMessage
- void toNative(
- @Bind Node node) {
- if (!PythonContext.get(node).isNativeAccessAllowed()) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw new RuntimeException(ErrorMessages.NATIVE_ACCESS_NOT_ALLOWED.toJavaStringUncached());
- }
- if (!isNative()) {
- setNativePointer(byteArrayToNativeInt8(getByteArray(), true));
- }
- }
- }
-
- /**
- * A native wrapper for arbitrary {@code int} arrays to be used like a {@code int *} pointer.
- */
- @ExportLibrary(InteropLibrary.class)
- @SuppressWarnings("static-method")
- public static final class CIntArrayWrapper extends CArrayWrapper {
-
- public CIntArrayWrapper(int[] delegate) {
- super(delegate);
- }
-
- public int[] getIntArray() {
- return ((int[]) getDelegate());
- }
-
- @ExportMessage
- long getArraySize() {
- return getIntArray().length;
- }
-
- @ExportMessage
- boolean hasArrayElements() {
- return true;
- }
-
- @ExportMessage
- Object readArrayElement(long index,
- @Exclusive @Cached GilNode gil) throws InvalidArrayIndexException {
- boolean mustRelease = gil.acquire();
- try {
- int idx = PInt.intValueExact(index);
- int[] arr = getIntArray();
- if (idx >= 0 && idx < arr.length) {
- return arr[idx];
- }
- } catch (OverflowException e) {
- // fall through
- } finally {
- gil.release(mustRelease);
- }
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw InvalidArrayIndexException.create(index);
- }
-
- @ExportMessage
- boolean isArrayElementReadable(long identifier) {
- return 0 <= identifier && identifier < getArraySize();
- }
-
- @ExportMessage
- void toNative(
- @Bind Node node) {
- if (!PythonContext.get(node).isNativeAccessAllowed()) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw new RuntimeException(ErrorMessages.NATIVE_ACCESS_NOT_ALLOWED.toJavaStringUncached());
- }
- if (!isNative()) {
- setNativePointer(intArrayToNativeInt32(getIntArray()));
- }
- }
- }
-}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtCommonNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtCommonNodes.java
index 639a875aa2..d200653e9b 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtCommonNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtCommonNodes.java
@@ -41,9 +41,14 @@
package com.oracle.graal.python.builtins.objects.cext.common;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writePtrField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readByteArrayElement;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readByteArrayElements;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readIntArrayElement;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readShortArrayElement;
import static com.oracle.graal.python.nodes.ErrorMessages.RETURNED_NULL_WO_SETTING_EXCEPTION;
import static com.oracle.graal.python.nodes.ErrorMessages.RETURNED_RESULT_WITH_EXCEPTION_SET;
-import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE;
import static com.oracle.graal.python.nodes.StringLiterals.T_IGNORE;
import static com.oracle.graal.python.nodes.StringLiterals.T_REPLACE;
import static com.oracle.graal.python.nodes.StringLiterals.T_STRICT;
@@ -56,41 +61,34 @@
import java.nio.charset.Charset;
import java.util.logging.Level;
-import org.graalvm.collections.Pair;
-
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
import com.oracle.graal.python.builtins.objects.bytes.BytesCommonBuiltins;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
-import com.oracle.graal.python.builtins.objects.cext.capi.CApiGuards;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
import com.oracle.graal.python.builtins.objects.cext.capi.PThreadState;
-import com.oracle.graal.python.builtins.objects.cext.capi.PrimitiveNativeWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CByteArrayWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.EnsureExecutableNodeGen;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.GetIndexNodeGen;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.ReadUnicodeArrayNodeGen;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.TransformPExceptionToNativeCachedNodeGen;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.exception.PBaseException;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotLen.CallSlotLenNode;
-import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
import com.oracle.graal.python.lib.PyNumberIndexNode;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
-import com.oracle.graal.python.nodes.SpecialMethodNames;
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
import com.oracle.graal.python.runtime.PythonContext;
@@ -102,8 +100,6 @@
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.util.OverflowException;
import com.oracle.graal.python.util.PythonUtils;
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
@@ -116,24 +112,16 @@
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateUncached;
-import com.oracle.truffle.api.dsl.Idempotent;
import com.oracle.truffle.api.dsl.ImportStatic;
-import com.oracle.truffle.api.dsl.NeverDefault;
-import com.oracle.truffle.api.dsl.NonIdempotent;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
-import com.oracle.truffle.api.nodes.DirectCallNode;
-import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
-import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.strings.TruffleString;
-import com.oracle.truffle.nfi.api.SignatureLibrary;
public abstract class CExtCommonNodes {
@@ -260,62 +248,58 @@ public static TruffleString getUTF32Name(int byteorder) {
@GenerateInline
@GenerateCached(false)
@GenerateUncached
- @ImportStatic(CApiGuards.class)
public abstract static class ReadUnicodeArrayNode extends PNodeWithContext {
- public abstract int[] execute(Node inliningTarget, Object array, int length, int elementSize);
+ public abstract int[] execute(Node inliningTarget, long array, int length, int elementSize);
- public static int[] executeUncached(Object array, int length, int elementSize) {
+ public static int[] executeUncached(long array, int length, int elementSize) {
return ReadUnicodeArrayNodeGen.getUncached().execute(null, array, length, elementSize);
}
@Specialization(guards = "elementSize == 1")
- static int[] read1(Node inliningTarget, Object array, int length, @SuppressWarnings("unused") int elementSize,
- @Shared @Cached InlinedConditionProfile calcLength,
- @Cached(inline = false) CStructAccess.ReadByteNode read) {
+ static int[] read1(Node inliningTarget, long array, int length, @SuppressWarnings("unused") int elementSize,
+ @Shared @Cached InlinedConditionProfile calcLength) {
int len = length;
if (calcLength.profile(inliningTarget, len == -1)) {
do {
len++;
- } while (read.readArrayElement(array, len) != 0);
+ } while (readByteArrayElement(array, len) != 0);
}
int[] result = new int[len];
for (int i = 0; i < len; i++) {
- result[i] = read.readArrayElement(array, i) & 0xFF;
+ result[i] = readByteArrayElement(array, i) & 0xFF;
}
return result;
}
@Specialization(guards = "elementSize == 2")
- static int[] read2(Node inliningTarget, Object array, int length, @SuppressWarnings("unused") int elementSize,
- @Shared @Cached InlinedConditionProfile calcLength,
- @Cached(inline = false) CStructAccess.ReadI16Node read) {
+ static int[] read2(Node inliningTarget, long array, int length, @SuppressWarnings("unused") int elementSize,
+ @Shared @Cached InlinedConditionProfile calcLength) {
int len = length;
if (calcLength.profile(inliningTarget, len == -1)) {
do {
len++;
- } while (read.readArrayElement(array, len) != 0);
+ } while (readShortArrayElement(array, len) != 0);
}
int[] result = new int[len];
for (int i = 0; i < len; i++) {
- result[i] = read.readArrayElement(array, i) & 0xFFFF;
+ result[i] = readShortArrayElement(array, i) & 0xFFFF;
}
return result;
}
@Specialization(guards = "elementSize == 4")
- static int[] read4(Node inliningTarget, Object array, int length, @SuppressWarnings("unused") int elementSize,
- @Shared @Cached InlinedConditionProfile calcLength,
- @Cached(inline = false) CStructAccess.ReadI32Node read) {
+ static int[] read4(Node inliningTarget, long array, int length, @SuppressWarnings("unused") int elementSize,
+ @Shared @Cached InlinedConditionProfile calcLength) {
int len = length;
if (calcLength.profile(inliningTarget, len == -1)) {
do {
len++;
- } while (read.readArrayElement(array, len) != 0);
+ } while (readIntArrayElement(array, len) != 0);
}
int[] result = new int[len];
for (int i = 0; i < len; i++) {
- result[i] = read.readArrayElement(array, i);
+ result[i] = readIntArrayElement(array, i);
}
return result;
}
@@ -324,7 +308,7 @@ static int[] read4(Node inliningTarget, Object array, int length, @SuppressWarni
@GenerateInline(inlineByDefault = true)
@GenerateCached
@GenerateUncached
- @ImportStatic({PGuards.class, CApiGuards.class})
+ @ImportStatic(PGuards.class)
public abstract static class ConvertPIntToPrimitiveNode extends Node {
public abstract Object execute(Node inliningTarget, Object o, int signed, int targetTypeSize, boolean exact);
@@ -349,37 +333,6 @@ public final int executeIntCached(Object o, int signed, int targetTypeSize) thro
return PGuards.expectInteger(execute(this, o, signed, targetTypeSize, true));
}
- @Specialization(guards = {"targetTypeSize == 4", "signed != 0", "fitsInInt32(nativeWrapper)"})
- @SuppressWarnings("unused")
- static int doWrapperToInt32(PrimitiveNativeWrapper nativeWrapper, int signed, int targetTypeSize, boolean exact) {
- return nativeWrapper.getInt();
- }
-
- @Specialization(guards = {"targetTypeSize == 4", "signed == 0", "fitsInUInt32(nativeWrapper)"})
- @SuppressWarnings("unused")
- static int doWrapperToUInt32Pos(PrimitiveNativeWrapper nativeWrapper, int signed, int targetTypeSize, boolean exact) {
- return nativeWrapper.getInt();
- }
-
- @Specialization(guards = {"targetTypeSize == 8", "signed != 0", "fitsInInt64(nativeWrapper)"})
- @SuppressWarnings("unused")
- static long doWrapperToInt64(PrimitiveNativeWrapper nativeWrapper, int signed, int targetTypeSize, boolean exact) {
- return nativeWrapper.getLong();
- }
-
- @Specialization(guards = {"targetTypeSize == 8", "signed == 0", "fitsInUInt64(nativeWrapper)"})
- @SuppressWarnings("unused")
- static long doWrapperToUInt64Pos(PrimitiveNativeWrapper nativeWrapper, int signed, int targetTypeSize, boolean exact) {
- return nativeWrapper.getLong();
- }
-
- @Specialization
- @SuppressWarnings("unused")
- static Object doWrapperGeneric(PrimitiveNativeWrapper nativeWrapper, int signed, int targetTypeSize, boolean exact,
- @Shared @Cached(inline = false) AsNativePrimitiveNode asNativePrimitiveNode) {
- return asNativePrimitiveNode.execute(nativeWrapper.getLong(), signed, targetTypeSize, exact);
- }
-
@Specialization
static Object doInt(int value, int signed, int targetTypeSize, boolean exact,
@Shared @Cached(inline = false) AsNativePrimitiveNode asNativePrimitiveNode) {
@@ -392,71 +345,11 @@ static Object doLong(long value, int signed, int targetTypeSize, boolean exact,
return asNativePrimitiveNode.execute(value, signed, targetTypeSize, exact);
}
- @Specialization(guards = {"!isPrimitiveNativeWrapper(obj)"}, replaces = {"doInt", "doLong"})
+ @Specialization(replaces = {"doInt", "doLong"})
static Object doOther(Object obj, int signed, int targetTypeSize, boolean exact,
@Shared @Cached(inline = false) AsNativePrimitiveNode asNativePrimitiveNode) {
return asNativePrimitiveNode.execute(obj, signed, targetTypeSize, exact);
}
-
- static boolean fitsInInt32(PrimitiveNativeWrapper nativeWrapper) {
- return nativeWrapper.isBool() || nativeWrapper.isInt();
- }
-
- static boolean fitsInInt64(PrimitiveNativeWrapper nativeWrapper) {
- return nativeWrapper.isIntLike() || nativeWrapper.isBool();
- }
-
- static boolean fitsInUInt32(PrimitiveNativeWrapper nativeWrapper) {
- return (nativeWrapper.isBool() || nativeWrapper.isInt()) && nativeWrapper.getInt() >= 0;
- }
-
- static boolean fitsInUInt64(PrimitiveNativeWrapper nativeWrapper) {
- return (nativeWrapper.isIntLike() || nativeWrapper.isBool()) && nativeWrapper.getLong() >= 0;
- }
- }
-
- /**
- * Converts a Python object to a Java double value (which is compatible to a C double).
- * This node is, for example, used to implement {@code PyFloat_AsDouble} or similar C API
- * functions and does coercion and may raise a Python exception if coercion fails.
- * Please note: In most cases, it is sufficient to use {@link PyFloatAsDoubleNode} but you might
- * want to use this node if the argument can be an object of type {@link PrimitiveNativeWrapper}
- * .
- */
- @GenerateInline(false) // footprint reduction 28 -> 10, inherits non-inlineable execute()
- @GenerateUncached
- @ImportStatic({SpecialMethodNames.class, CApiGuards.class})
- public abstract static class AsNativeDoubleNode extends CExtToNativeNode {
- public abstract double executeDouble(Object arg);
-
- @Specialization(guards = "!isNativeWrapper(value)")
- static double runGeneric(Object value,
- @Bind Node inliningTarget,
- @Cached PyFloatAsDoubleNode asDoubleNode) {
- // IMPORTANT: this should implement the behavior like 'PyFloat_AsDouble'. So, if it
- // is a float object, use the value and do *NOT* call '__float__'.
- return asDoubleNode.execute(null, inliningTarget, value);
- }
-
- @Specialization(guards = "!object.isDouble()")
- static double doLongNativeWrapper(PrimitiveNativeWrapper object) {
- return object.getLong();
- }
-
- @Specialization(guards = "object.isDouble()")
- static double doDoubleNativeWrapper(PrimitiveNativeWrapper object) {
- return object.getDouble();
- }
- }
-
- public abstract static class CheckFunctionResultNode extends PNodeWithContext {
-
- public final Object execute(PythonContext context, TruffleString name, Object result) {
- PythonLanguage language = context.getLanguage(this);
- return execute(context.getThreadState(language), name, result);
- }
-
- public abstract Object execute(PythonThreadState threadState, TruffleString name, Object result);
}
/**
@@ -471,6 +364,7 @@ public abstract static class TransformExceptionToNativeNode extends Node {
public abstract void execute(Node inliningTarget, Object pythonException);
+ @TruffleBoundary
public static void executeUncached(Object pythonException) {
CExtCommonNodesFactory.TransformExceptionToNativeNodeGen.getUncached().execute(null, pythonException);
}
@@ -479,17 +373,15 @@ public static void executeUncached(Object pythonException) {
static void setCurrentException(Node inliningTarget, Object pythonException,
@Cached GetThreadStateNode getThreadStateNode,
@Cached CExtNodes.XDecRefPointerNode decRefPointerNode,
- @Cached(inline = false) PythonToNativeNewRefNode pythonToNativeNode,
- @Cached(inline = false) CStructAccess.ReadPointerNode readPointerNode,
- @Cached(inline = false) CStructAccess.WritePointerNode writePointerNode) {
+ @Cached(inline = false) PythonToNativeNewRefNode pythonToNativeNode) {
/*
* Run the ToNative conversion early so that the reference poll won't interrupt between
* the read and write.
*/
- Object currentException = pythonToNativeNode.execute(pythonException);
- Object nativeThreadState = PThreadState.getOrCreateNativeThreadState(getThreadStateNode.execute(inliningTarget));
- Object oldException = readPointerNode.read(nativeThreadState, CFields.PyThreadState__current_exception);
- writePointerNode.write(nativeThreadState, CFields.PyThreadState__current_exception, currentException);
+ long currentException = pythonToNativeNode.executeLong(pythonException);
+ long nativeThreadState = PThreadState.getOrCreateNativeThreadState(getThreadStateNode.execute(inliningTarget));
+ long oldException = readPtrField(nativeThreadState, CFields.PyThreadState__current_exception);
+ writePtrField(nativeThreadState, CFields.PyThreadState__current_exception, currentException);
decRefPointerNode.execute(inliningTarget, oldException);
}
}
@@ -503,6 +395,11 @@ static void setCurrentException(Node inliningTarget, Object pythonException,
public abstract static class TransformPExceptionToNativeNode extends Node {
public abstract void execute(Node inliningTarget, PException e);
+ @TruffleBoundary(allowInlining = true)
+ public static void executeUncached(PException ex) {
+ CExtCommonNodesFactory.TransformPExceptionToNativeNodeGen.getUncached().execute(null, ex);
+ }
+
@Specialization
static void setCurrentException(Node inliningTarget, PException ex,
@Cached TransformExceptionToNativeNode transformNode) {
@@ -546,13 +443,12 @@ public static Object executeUncached(PythonThreadState threadState) {
@Specialization
static Object getException(PythonThreadState threadState,
- @Cached(inline = false) CStructAccess.ReadPointerNode readPointerNode,
- @Cached(inline = false) CStructAccess.WritePointerNode writePointerNode,
@Cached CApiTransitions.NativeToPythonTransferNode nativeToPythonNode) {
- Object nativeThreadState = PThreadState.getNativeThreadState(threadState);
- if (nativeThreadState != null) {
- Object exception = nativeToPythonNode.execute(readPointerNode.read(nativeThreadState, CFields.PyThreadState__current_exception));
- writePointerNode.write(nativeThreadState, CFields.PyThreadState__current_exception, 0L);
+ long nativeThreadState = threadState.getNativePointer();
+ if (nativeThreadState != PythonAbstractObject.UNINITIALIZED) {
+ assert nativeThreadState != PythonAbstractObject.NATIVE_POINTER_FREED;
+ Object exception = nativeToPythonNode.execute(readPtrField(nativeThreadState, CFields.PyThreadState__current_exception));
+ writePtrField(nativeThreadState, CFields.PyThreadState__current_exception, 0L);
return exception;
}
return PNone.NO_VALUE;
@@ -564,6 +460,11 @@ static Object getException(PythonThreadState threadState,
@GenerateCached(false)
public abstract static class TransformExceptionFromNativeNode extends Node {
+ @TruffleBoundary
+ public static void executeUncached(PythonThreadState threadState, TruffleString name, boolean indicatesError, boolean strict) {
+ TransformExceptionFromNativeNode.getUncached().execute(null, threadState, name, indicatesError, strict);
+ }
+
/**
* Checks the current exception state with respect to flag {@code indicatesError} (and
* {@code strict}).
@@ -646,32 +547,8 @@ public static TransformExceptionFromNativeNode getUncached() {
}
}
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- public abstract static class GetByteArrayNode extends Node {
-
- public abstract byte[] execute(Node inliningTarget, Object obj, long n) throws InteropException, OverflowException;
-
- @Specialization
- static byte[] doCArrayWrapper(CByteArrayWrapper obj, long n) {
- return subRangeIfNeeded(obj.getByteArray(), n);
- }
-
- @Specialization
- static byte[] doForeign(Object obj, long n,
- @Cached(inline = false) CStructAccess.ReadByteNode readNode) {
- return readNode.readByteArray(obj, (int) n);
- }
-
- private static byte[] subRangeIfNeeded(byte[] bytes, long n) {
- if (bytes.length > n && n >= 0) {
- // cast to int is guaranteed because of 'bytes.length > n'
- return PythonUtils.arrayCopyOf(bytes, (int) n);
- } else {
- return bytes;
- }
- }
+ public static byte[] getByteArray(long ptr, long n) throws OverflowException {
+ return readByteArrayElements(ptr, 0, PInt.intValueExact(n));
}
/**
@@ -821,12 +698,6 @@ static int doLongToInt32Lossy(long obj, int signed, int targetTypeSize, boolean
return (int) obj;
}
- @Specialization(guards = "targetTypeSize == 8")
- @SuppressWarnings("unused")
- static Object doVoidPtrToI64(PythonNativeVoidPtr obj, int signed, int targetTypeSize, boolean exact) {
- return obj;
- }
-
@Specialization(guards = {"exact", "targetTypeSize == 4"})
@SuppressWarnings("unused")
@TruffleBoundary
@@ -886,7 +757,6 @@ static long doPIntToInt64Lossy(PInt obj, int signed, int targetTypeSize, boolean
"doIntToInt64", "doIntToUInt64Pos", "doIntToUInt64", //
"doLongToInt64", "doLongToUInt64Pos", "doLongToUInt64", //
"doLongToInt32Exact", "doLongToUInt32PosExact", "doLongToUInt32Exact", "doLongToInt32Lossy", //
- "doVoidPtrToI64", //
"doPIntTo32Bit", "doPIntTo64Bit", "doPIntToInt32Lossy", "doPIntToInt64Lossy"})
static Object doGeneric(Object obj, int signed, int targetTypeSize, boolean exact,
@Bind Node inliningTarget,
@@ -945,9 +815,6 @@ private static int toInt32(Node inliningTarget, Object object, int signed, boole
return doPIntTo32Bit(pval, signed, 4, true, inliningTarget, raiseNode);
}
return doPIntToInt32Lossy(pval, signed, 4, false);
- } else if (object instanceof PythonNativeVoidPtr) {
- // that's just not possible
- throw raiseNode.raise(inliningTarget, PythonErrorType.OverflowError, ErrorMessages.PYTHON_INT_TOO_LARGE_TO_CONV_TO_C_TYPE, 4);
}
throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.INDEX_RETURNED_NON_INT, object);
}
@@ -975,8 +842,6 @@ private static Object toInt64(Node inliningTarget, Object object, int signed, bo
return doPIntTo64Bit(pval, signed, 8, true, inliningTarget, raiseNode);
}
return doPIntToInt64Lossy(pval, signed, 8, false);
- } else if (object instanceof PythonNativeVoidPtr) {
- return doVoidPtrToI64((PythonNativeVoidPtr) object, signed, 8, exact);
}
throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.INDEX_RETURNED_NON_INT, object);
}
@@ -990,6 +855,7 @@ private static Object toInt64(Node inliningTarget, Object object, int signed, bo
*/
@GenerateInline(false) // footprint reduction 32 -> 13, inherits non-inlineable execute()
@GenerateUncached
+ @ImportStatic(NativeMemory.class)
public abstract static class StringAsPythonStringNode extends CExtToJavaNode {
@Specialization
@@ -1005,16 +871,16 @@ static TruffleString doTruffleString(TruffleString value) {
}
@SuppressWarnings("unused")
- @Specialization(guards = "interopLib.isNull(value)", limit = "3")
- static Object doGeneric(Object value,
- @CachedLibrary("value") InteropLibrary interopLib) {
+ @Specialization(guards = "value == NULLPTR")
+ static Object doGeneric(long value) {
return PNone.NONE;
}
@Specialization
- static TruffleString doNative(Object value,
+ static TruffleString doNative(long value,
+ @Bind Node inliningTarget,
@Cached FromCharPointerNode fromPtr) {
- return fromPtr.execute(value);
+ return fromPtr.execute(inliningTarget, value);
}
}
@@ -1247,190 +1113,25 @@ public static GetIndexNode create() {
}
}
- /**
- * Use this node to coerce an object (that is expected to be one of the pointer representations
- * we use) into a {@code long} value. This node is semantically the same as method
- * {@link PythonUtils#coerceToLong(Object, InteropLibrary)} but does profiling of the pointer
- * object and additionally avoids the {@code InteropLibrary} for our known type
- * {@link NativePointer}.
- */
- @GenerateUncached
- @GenerateInline
- @GenerateCached(false)
- public abstract static class CoerceNativePointerToLongNode extends Node {
-
- public static long executeUncached(Object pointerObject) {
- return CExtCommonNodesFactory.CoerceNativePointerToLongNodeGen.getUncached().execute(null, pointerObject);
- }
-
- public abstract long execute(Node inliningTarget, Object pointerObject);
-
- @Specialization
- static long doLong(Long l) {
- return l;
- }
-
- @Specialization
- static long doNativePointer(NativePointer nativePointer) {
- return nativePointer.asPointer();
- }
-
- @Specialization(guards = "!isNativePointer(pointerObject)", limit = "3")
- static long doOther(Object pointerObject,
- @CachedLibrary("pointerObject") InteropLibrary lib) {
- return PythonUtils.coerceToLong(pointerObject, lib);
- }
-
- static boolean isNativePointer(Object pointerObject) {
- return pointerObject instanceof NativePointer;
- }
- }
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(CExtContext.class);
/**
- * This unwraps foreign pointer objects (e.g. LLVM pointers) if they respond to
- * {@link InteropLibrary#isPointer(Object)} with {@code true} and creates a new
- * {@link NativePointer} object with the long value. This is useful to avoid unnecessary
- * indirections.
- */
- @GenerateUncached
- @GenerateInline
- @GenerateCached(false)
- public abstract static class UnwrapForeignPointerNode extends Node {
-
- public abstract Object execute(Node inliningTarget, Object pointerObject);
-
- @Specialization(limit = "3")
- static Object doOther(Object pointerObject,
- @CachedLibrary("pointerObject") InteropLibrary lib) {
- if (lib.isPointer(pointerObject)) {
- try {
- return new NativePointer(lib.asPointer(pointerObject));
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
- // This is usually the path for managed mode. We expect a backend pointer object.
- assert CApiTransitions.isBackendPointerObject(pointerObject);
- return pointerObject;
- }
- }
-
- /**
- * Ensures that the given pointer object is an executable interop value.
+ * Binds a native pointer with a signature to a typed native function pointer.
*
*
* NOTE: This method will fail if {@link PythonContext#isNativeAccessAllowed() native
- * access} is not allowed and if {@code callable} is yet not
- * {@link InteropLibrary#isExecutable(Object) executable}.
- *
- *
- * If the {@code callable} is not {@link InteropLibrary#isExecutable(Object) executable}, the
- * provided {@link NativeCExtSymbol signature} will be used to bind the object an executable
- * {@code NFI} pointer.
+ * access} is not allowed
*
*/
- @GenerateInline
- @GenerateCached(false)
- @GenerateUncached
- public abstract static class EnsureExecutableNode extends Node {
- private static final TruffleLogger LOGGER = CApiContext.getLogger(CExtContext.class);
-
- public static Object executeUncached(Object callable, NativeCExtSymbol descriptor) {
- return EnsureExecutableNodeGen.getUncached().execute(null, callable, descriptor);
+ @TruffleBoundary
+ public static NativeFunctionPointer bindFunctionPointer(long pointer, NativeCExtSymbol descriptor) {
+ PythonContext pythonContext = PythonContext.get(null);
+ if (!pythonContext.isNativeAccessAllowed()) {
+ LOGGER.severe(PythonUtils.formatJString("Attempting to bind %s to an NFI signature but native access is not allowed", pointer));
}
-
- /**
- * @param inliningTarget The inlining target.
- * @param callable The callable to ensure that it is executable.
- * @param descriptor The descriptor describing the signature to bind to if the object is not
- * executable.
- * @return An interop object that is {@link InteropLibrary#isExecutable(Object) executable}.
- */
- public abstract Object execute(Node inliningTarget, Object callable, NativeCExtSymbol descriptor);
-
- @Specialization(guards = {"descriptor == cachedDescriptor", "withPanama(inliningTarget) == cachedWithPanama", "!isExecutable(lib, callable)"}, limit = "3")
- static Object doBind(Node inliningTarget, Object callable, @SuppressWarnings("unused") NativeCExtSymbol descriptor,
- @SuppressWarnings("unused") @Cached("descriptor") NativeCExtSymbol cachedDescriptor,
- @SuppressWarnings("unused") @Cached("withPanama(inliningTarget)") boolean cachedWithPanama,
- @SuppressWarnings("unused") @Shared @CachedLibrary(limit = "3") InteropLibrary lib,
- @Shared @Cached UnwrapForeignPointerNode unwrapForeignPointerNode,
- @Shared @CachedLibrary(limit = "1") SignatureLibrary signatureLib,
- @Cached("createFactory(descriptor)") DirectCallNode nfiSignatureFactory) {
- /*
- * Since we mix native and LLVM execution, it happens that 'callable' is an LLVM pointer
- * (that is still not executable). To avoid unnecessary indirections, we test
- * 'isPointer(callable)' and if so, we retrieve the bare long value using
- * 'asPointer(callable)' and wrap it in our own NativePointer.
- */
- Object funPtr = unwrapForeignPointerNode.execute(inliningTarget, callable);
- if (LOGGER.isLoggable(Level.FINER)) {
- LOGGER.finer(PythonUtils.formatJString("Binding %s (signature: %s) to NFI signature %s", callable, descriptor.getName(), descriptor.getSignature()));
- }
- return signatureLib.bind(nfiSignatureFactory.call(), funPtr);
- }
-
- @Specialization(guards = "lib.isExecutable(callable)")
- @SuppressWarnings("unused")
- static Object doNothing(Object callable, NativeCExtSymbol descriptor,
- @Shared @CachedLibrary(limit = "3") InteropLibrary lib) {
- return callable;
- }
-
- @Specialization(replaces = {"doBind", "doNothing"})
- static Object doGeneric(Node inliningTarget, Object callable, NativeCExtSymbol descriptor,
- @Shared @CachedLibrary(limit = "3") InteropLibrary lib,
- @Shared @Cached UnwrapForeignPointerNode unwrapForeignPointerNode,
- @Shared @CachedLibrary(limit = "1") SignatureLibrary signatureLib,
- @Cached IndirectCallNode nfiSignatureFactory) {
- PythonContext pythonContext = PythonContext.get(inliningTarget);
- if (!lib.isExecutable(callable)) {
- if (!pythonContext.isNativeAccessAllowed()) {
- LOGGER.severe(PythonUtils.formatJString("Attempting to bind %s to an NFI signature but native access is not allowed", callable));
- }
- // see 'doBind' for explanation
- Object funPtr = unwrapForeignPointerNode.execute(inliningTarget, callable);
- if (LOGGER.isLoggable(Level.FINER)) {
- LOGGER.finer(PythonUtils.formatJString("Binding %s (signature: %s) to NFI signature %s", callable, descriptor.getName(), descriptor.getSignature()));
- }
- return signatureLib.bind(nfiSignatureFactory.call(getCallTarget(pythonContext, descriptor)), funPtr);
- }
- return callable;
- }
-
- private static Source getSource(PythonLanguage language, boolean panama, NativeCExtSymbol descriptor) {
- CompilerAsserts.neverPartOfCompilation();
-
- assert descriptor.getSignature() != null && !descriptor.getSignature().isEmpty();
- String src = (panama ? "with panama " : "") + descriptor.getSignature();
- return language.getOrCreateSource(EnsureExecutableNode::buildNFISource, Pair.create(src, descriptor.getName()));
- }
-
- // TODO(fa): we could avoid this boundary by storing the sources to the NativeCExtSymbol
- @TruffleBoundary
- private static CallTarget getCallTarget(PythonContext pythonContext, NativeCExtSymbol descriptor) {
- Source source = getSource(pythonContext.getLanguage(), pythonContext.getOption(PythonOptions.UsePanama), descriptor);
- return pythonContext.getEnv().parseInternal(source);
- }
-
- @NeverDefault
- static DirectCallNode createFactory(NativeCExtSymbol descriptor) {
- CompilerAsserts.neverPartOfCompilation();
- return DirectCallNode.create(getCallTarget(PythonContext.get(null), descriptor));
- }
-
- @NonIdempotent
- static boolean withPanama(Node inliningTarget) {
- return PythonContext.get(inliningTarget).getOption(PythonOptions.UsePanama);
- }
-
- @Idempotent
- static boolean isExecutable(InteropLibrary lib, Object object) {
- return lib.isExecutable(object);
- }
-
- private static Source buildNFISource(Object key) {
- Pair, ?> srcAndName = (Pair, ?>) key;
- return Source.newBuilder(J_NFI_LANGUAGE, (String) srcAndName.getLeft(), (String) srcAndName.getRight()).internal(true).build();
+ if (LOGGER.isLoggable(Level.FINER)) {
+ LOGGER.finer(PythonUtils.formatJString("Binding %s to native callable %s", pointer, descriptor.getName()));
}
+ return descriptor.bind(pythonContext.ensureNativeContext(), pointer);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtContext.java
index 7a99e716c3..2ea0d533f1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, 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
@@ -47,15 +47,11 @@
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.objects.cext.common.LoadCExtException.ImportException;
-import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes;
import com.oracle.graal.python.builtins.objects.exception.PBaseException;
-import com.oracle.graal.python.nodes.ErrorMessages;
-import com.oracle.graal.python.nodes.SpecialMethodNames;
-import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode.LookupAndCallUnaryDynamicNode;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.exception.ExceptionUtils;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -78,10 +74,10 @@ public abstract class CExtContext {
private final PythonContext context;
/** The library object representing 'libpython.*.so' or similar. */
- private final Object library;
+ private final NativeLibrary library;
private final String libraryName;
- public CExtContext(PythonContext context, Object library, String libraryName) {
+ public CExtContext(PythonContext context, NativeLibrary library, String libraryName) {
this.context = context;
this.library = library;
this.libraryName = libraryName;
@@ -91,7 +87,7 @@ public final PythonContext getContext() {
return context;
}
- public final Object getLibrary() {
+ public final NativeLibrary getLibrary() {
return library;
}
@@ -131,8 +127,12 @@ public static boolean isMethStatic(int flags) {
return (flags & METH_STATIC) != 0;
}
+ public static boolean isMethClass(int flags) {
+ return (flags & METH_CLASS) != 0;
+ }
+
public static boolean isClassOrStaticMethod(int flags) {
- return flags > 0 && (flags & (METH_CLASS | METH_STATIC)) != 0;
+ return isMethClass(flags) || isMethStatic(flags);
}
@TruffleBoundary
@@ -151,43 +151,6 @@ protected static TruffleString getBaseName(TruffleString name) {
return name.substringUncached(idx + 1, len - idx - 1, TS_ENCODING, true);
}
- @TruffleBoundary
- protected static PException reportImportError(RuntimeException e, TruffleString name, TruffleString path) throws ImportException {
- StringBuilder sb = new StringBuilder();
- Object pythonCause = null;
- PException pcause = null;
- if (e instanceof PException) {
- Object excObj = ((PException) e).getEscapedException();
- pythonCause = excObj;
- pcause = (PException) e;
- sb.append(LookupAndCallUnaryDynamicNode.getUncached().executeObject(excObj, SpecialMethodNames.T___REPR__));
- } else {
- // that call will cause problems if the format string contains '%p'
- sb.append(e.getMessage());
- }
- Throwable cause = e;
- while ((cause = cause.getCause()) != null) {
- if (e instanceof PException) {
- Object pythonException = ((PException) e).getEscapedException();
- if (pythonCause != null) {
- ExceptionNodes.SetCauseNode.executeUncached(pythonCause, pythonException);
- }
- pythonCause = pythonException;
- pcause = (PException) e;
- }
- if (cause.getMessage() != null) {
- sb.append(", ");
- sb.append(cause.getMessage());
- }
- }
- Object[] args = new Object[]{path, sb.toString()};
- if (pythonCause != null) {
- throw new ImportException(pcause, name, path, ErrorMessages.CANNOT_LOAD, args);
- } else {
- throw new ImportException(null, name, path, ErrorMessages.CANNOT_LOAD, args);
- }
- }
-
@TruffleBoundary
public static PException wrapJavaException(Throwable e, Node raisingNode) {
TruffleString message = toTruffleStringUncached(e.getMessage());
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtToJavaNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtToJavaNode.java
index d3135e1fa4..6efd66acfb 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtToJavaNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtToJavaNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021, 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
@@ -40,5 +40,8 @@
*/
package com.oracle.graal.python.builtins.objects.cext.common;
-public abstract class CExtToJavaNode extends CExtAsPythonObjectNode {
+import com.oracle.graal.python.nodes.PNodeWithContext;
+
+public abstract class CExtToJavaNode extends PNodeWithContext {
+ public abstract Object execute(Object object);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativeCExtSymbol.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativeCExtSymbol.java
index 74eb8baa34..e6833a9631 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativeCExtSymbol.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativeCExtSymbol.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -40,6 +40,10 @@
*/
package com.oracle.graal.python.builtins.objects.cext.common;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
+import com.oracle.graal.python.runtime.nativeaccess.NativeContext;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
+import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType;
import com.oracle.truffle.api.strings.TruffleString;
public interface NativeCExtSymbol {
@@ -47,8 +51,20 @@ public interface NativeCExtSymbol {
TruffleString getTsName();
- /**
- * Returns the NFI signature.
- */
- String getSignature();
+ ArgDescriptor getReturnValue();
+
+ ArgDescriptor[] getArguments();
+
+ default NativeFunctionPointer bind(NativeContext context, long pointer) {
+ ArgDescriptor returnValue = getReturnValue();
+ if (returnValue == null) {
+ throw new UnsupportedOperationException("No signature for " + getName());
+ }
+ ArgDescriptor[] arguments = getArguments();
+ NativeSimpleType[] argTypes = new NativeSimpleType[arguments.length];
+ for (int i = 0; i < arguments.length; i++) {
+ argTypes[i] = arguments[i].getNativeSimpleType();
+ }
+ return NativeFunctionPointer.create(context, pointer, returnValue.getNativeSimpleType(), argTypes);
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativePointer.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativePointer.java
index afe3b5e1e4..893169a2ab 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativePointer.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativePointer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, 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
@@ -48,15 +48,14 @@
import com.oracle.truffle.api.library.ExportMessage;
/**
- * This class can be used to bridge between JNI and LLVM. In particular, we use it to wrap native
- * pointers received from JNI. In this way, the pointers can be used with code written for LLVM.
- * Ultimately, this class should go away once JNI and LLVM backends are untangled.
+ * Currently only used for wrapping pointers to call TruffleString, see GR-71311 and in
+ * InvokeArrowReleaseCallbackNode which still uses original NFI.
*/
@ExportLibrary(InteropLibrary.class)
public final class NativePointer implements TruffleObject {
private final long ptr;
- public NativePointer(long ptr) {
+ private NativePointer(long ptr) {
/*
* Instances of this type may only be created if native access is allowed because other code
* relies on that and may use Unsafe without further checking.
@@ -65,16 +64,8 @@ public NativePointer(long ptr) {
this.ptr = ptr;
}
- private NativePointer() {
- this.ptr = 0;
- }
-
- /**
- * Returns an object representing a {@code NULL} pointer. This may also be used if
- * {@link PythonContext#isNativeAccessAllowed()} is {@code false}.
- */
- public static NativePointer createNull() {
- return new NativePointer();
+ public static NativePointer wrap(long ptr) {
+ return new NativePointer(ptr);
}
@ExportMessage
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CConstants.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CConstants.java
index f4ee44d620..ae7711df44 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CConstants.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CConstants.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, 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,13 +41,16 @@
package com.oracle.graal.python.builtins.objects.cext.structs;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readLongArrayElements;
import static com.oracle.graal.python.nodes.ErrorMessages.INTERNAL_INT_OVERFLOW;
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
import com.oracle.graal.python.annotations.CApiConstants;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
@@ -99,8 +102,14 @@ public int intValue() {
private static void resolve() {
CompilerAsserts.neverPartOfCompilation();
- Object constantsPointer = PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_PYTRUFFLE_CONSTANTS);
- long[] constants = CStructAccessFactory.ReadI64NodeGen.getUncached().readLongArray(constantsPointer, VALUES.length);
+ long constantsPointer;
+ try {
+ NativeFunctionPointer constants = CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_PYTRUFFLE_CONSTANTS);
+ constantsPointer = ExternalFunctionInvoker.invokePYTRUFFLE_CONSTANTS(constants.getAddress());
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
+ long[] constants = readLongArrayElements(constantsPointer, 0L, VALUES.length);
for (CConstants constant : VALUES) {
constant.longValue = constants[constant.ordinal()];
if (constant.longValue == -1) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CFields.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CFields.java
index 270d2cb8b7..8804568e1b 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CFields.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CFields.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, 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
@@ -101,9 +101,11 @@
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.traverseproc;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.unaryfunc;
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.vectorcallfunc;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readLongArrayElements;
import com.oracle.graal.python.annotations.CApiFields;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
import com.oracle.truffle.api.CompilerAsserts;
@@ -354,10 +356,15 @@ public enum CFields {
PyThreadState__dict(PyObject),
PyThreadState__small_ints(PyObjectPtr),
PyThreadState__gc(Pointer),
+ PyThreadState__graalpy_deallocating(Pointer),
PyThreadState__py_recursion_limit(Int),
PyThreadState__py_recursion_remaining(Int),
PyThreadState__c_recursion_remaining(Int),
+ GraalPyDeallocState__items(PyObjectPtr),
+ GraalPyDeallocState__len(Int),
+ GraalPyDeallocState__capacity(Int),
+
GCState__enabled(Int),
GCState__debug(Int),
GCState__generations(Pointer),
@@ -398,7 +405,7 @@ public enum CFields {
@CompilationFinal private long offset = -1;
- long offset() {
+ public long offset() {
long o = offset;
if (o == -1) {
CompilerDirectives.transferToInterpreterAndInvalidate();
@@ -414,8 +421,14 @@ CStructs struct() {
private static void resolve() {
CompilerAsserts.neverPartOfCompilation();
- Object offsetsPointer = PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_PYTRUFFLE_STRUCT_OFFSETS);
- long[] offsets = CStructAccessFactory.ReadI64NodeGen.getUncached().readLongArray(offsetsPointer, VALUES.length);
+ long offsetsPointer;
+ try {
+ offsetsPointer = ExternalFunctionInvoker.invokePYTRUFFLE_STRUCT_OFFSETS(
+ CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_PYTRUFFLE_STRUCT_OFFSETS).getAddress());
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
+ long[] offsets = readLongArrayElements(offsetsPointer, 0L, VALUES.length);
for (CFields field : VALUES) {
field.offset = offsets[field.ordinal()];
assert field.offset >= 0 && field.offset < 1024;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructAccess.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructAccess.java
index 2bd02ac262..6c9f896557 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructAccess.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructAccess.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, 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
@@ -40,1341 +40,268 @@
*/
package com.oracle.graal.python.builtins.objects.cext.structs;
-import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
-import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.POINTER_SIZE;
+import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;
+
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
-import com.oracle.graal.python.builtins.objects.cext.capi.PySequenceArrayWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativePtrToPythonNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CoerceNativePointerToLongNode;
-import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.AllocateNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.FreeNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.GetElementPtrNodeGen;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.ReadCharPtrNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.ReadI32NodeGen;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.ReadObjectNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.ReadPointerNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.WriteIntNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.WriteLongNodeGen;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.WritePointerNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccessFactory.WriteTruffleStringNodeGen;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.PGuards;
-import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ImportStatic;
-import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.strings.TruffleString;
-import sun.misc.Unsafe;
-
@SuppressWarnings("truffle-inlining")
public class CStructAccess {
- private static boolean validPointer(Object pointer) {
- return !(pointer instanceof PythonAbstractObject) && !(pointer instanceof PythonNativeWrapper);
+ public static long allocate(CStructs struct) {
+ return NativeMemory.calloc(struct.size());
}
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class AllocateNode extends Node {
-
- abstract Object execute(long count, long elsize, boolean allocatePyMem);
-
- public final Object alloc(CStructs struct) {
- return execute(1, struct.size(), false);
- }
-
- public final Object calloc(long count, CStructs elstruct) {
- return execute(count, elstruct.size(), false);
- }
-
- public final Object alloc(CStructs struct, boolean allocatePyMem) {
- return execute(1, struct.size(), allocatePyMem);
- }
-
- public final Object alloc(long size) {
- return execute(1, size, false);
- }
-
- public final Object calloc(long count, long elSize) {
- return execute(count, elSize, false);
- }
-
- public Object alloc(int size, boolean allocatePyMem) {
- return execute(1, size, allocatePyMem);
- }
-
- @Specialization(guards = "!allocatePyMem")
- static Object allocLong(long count, long size, @SuppressWarnings("unused") boolean allocatePyMem,
- @Bind Node inliningTarget,
- @Cached InlinedBranchProfile overflowProfile) {
- assert count >= 0;
- assert size >= 0;
- // non-zero size to get unique pointers
- try {
- long totalSize = Math.multiplyExact(count, size);
- long memory = allocUncachedPointer(totalSize);
- return new NativePointer(memory);
- } catch (ArithmeticException e) {
- overflowProfile.enter(inliningTarget);
- return NativePointer.createNull();
- }
- }
-
- @Specialization(guards = "allocatePyMem")
- static Object allocLongPyMem(long count, long elsize, @SuppressWarnings("unused") boolean allocatePyMem,
- @Cached PCallCapiFunction call) {
- assert elsize >= 0;
- return call.call(NativeCAPISymbol.FUN_PYMEM_ALLOC, count, elsize);
- }
-
- public static Object allocUncached(CStructs struct) {
- return AllocateNodeGen.getUncached().alloc(struct);
- }
-
- public static Object callocUncached(long count, CStructs elstruct) {
- return AllocateNodeGen.getUncached().calloc(count, elstruct);
- }
-
- public static Object allocUncached(long size) {
- return AllocateNodeGen.getUncached().alloc(size);
- }
-
- public static long allocUncachedPointer(long size) {
- long memory = UNSAFE.allocateMemory(size == 0 ? 1 : size);
- UNSAFE.setMemory(memory, size, (byte) 0);
- return memory;
- }
-
- public static Object callocUncached(long count, long elSize) {
- return AllocateNodeGen.getUncached().calloc(count, elSize);
- }
+ public static long getFieldPtr(long structBasePtr, CFields field) {
+ return NativeMemory.getFieldPtr(structBasePtr, field.offset());
}
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class FreeNode extends Node {
-
- public static void executeUncached(Object pointer) {
- CStructAccessFactory.FreeNodeGen.getUncached().execute(pointer);
- }
-
- abstract void execute(Object pointer);
-
- public final void free(Object pointer) {
- execute(pointer);
- }
-
- @Specialization
- public static void freeLong(long pointer) {
- UNSAFE.freeMemory(pointer);
- }
-
- @Specialization
- static void freeNativePointer(NativePointer pointer) {
- UNSAFE.freeMemory(pointer.asPointer());
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static void freePointer(Object pointer,
- @CachedLibrary("pointer") InteropLibrary lib) {
- UNSAFE.freeMemory(asPointer(pointer, lib));
- }
-
- @NeverDefault
- public static FreeNode create() {
- return FreeNodeGen.create();
- }
-
- public static FreeNode getUncached() {
- return FreeNodeGen.getUncached();
- }
+ public static byte readByteField(long structBasePtr, CFields field) {
+ assert field.type.isI8();
+ return NativeMemory.readByte(getFieldPtr(structBasePtr, field));
}
- public abstract static class ReadBaseNode extends Node implements CStructAccessNode {
-
- abstract Object executeGeneric(Object pointer, long offset);
-
- public final Object readGeneric(Object pointer, long offset) {
- return executeGeneric(pointer, offset);
- }
+ public static void writeByteField(long structBasePtr, CFields field, byte value) {
+ assert field.type.isI8();
+ NativeMemory.writeByte(getFieldPtr(structBasePtr, field), value);
}
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class GetElementPtrNode extends ReadBaseNode {
-
- abstract Object execute(Object pointer, long offset);
-
- public final Object getElementPtr(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return true;
- }
-
- @Specialization
- static long getLong(long pointer, long offset) {
- return pointer + offset;
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static long getPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib) {
- return getLong(asPointer(pointer, lib), offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static Object getManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- return call.call(NativeCAPISymbol.FUN_PTR_ADD, pointer, offset);
- }
-
- public static GetElementPtrNode getUncached() {
- return GetElementPtrNodeGen.getUncached();
- }
+ public static int readIntField(long structBasePtr, CFields field) {
+ assert field.type.isI32();
+ return NativeMemory.readInt(getFieldPtr(structBasePtr, field));
}
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class ReadByteNode extends ReadBaseNode {
-
- abstract byte execute(Object pointer, long offset);
-
- @Override
- final Object executeGeneric(Object pointer, long offset) {
- byte value = execute(pointer, offset);
- return isCharSigned() ? (int) value : Byte.toUnsignedInt(value);
- }
-
- public final byte read(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
-
- public final byte readFromObj(PythonNativeObject self, CFields field) {
- return read(self.getPtr(), field);
- }
-
- public final int readFromObjUnsigned(PythonNativeObject self, CFields field) {
- return Byte.toUnsignedInt(read(self.getPtr(), field));
- }
-
- public final int readFromObjUnsigned(PythonNativeObject self, CFields field, int offset) {
- return Byte.toUnsignedInt(execute(self.getPtr(), field.offset() + offset));
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI8();
- }
-
- public final byte[] readByteArray(Object pointer, int elements) {
- return readByteArray(pointer, elements, 0);
- }
-
- public final byte[] readByteArray(Object pointer, int elements, long sourceOffset) {
- byte[] result = new byte[elements];
- for (int i = 0; i < result.length; i++) {
- result[i] = execute(pointer, (i + sourceOffset) * Byte.BYTES);
- }
- return result;
- }
-
- public final void readByteArray(Object pointer, byte[] target, int length, long sourceOffset, int targetOffset) {
- for (int i = 0; i < length; i++) {
- target[i + targetOffset] = execute(pointer, (i + sourceOffset) * Byte.BYTES);
- }
- }
+ public static void writeIntField(long structBasePtr, CFields field, int value) {
+ assert field.type.isI32();
+ NativeMemory.writeInt(getFieldPtr(structBasePtr, field), value);
+ }
- public final byte readArrayElement(Object pointer, long element) {
- return execute(pointer, element);
- }
+ public static int readStructArrayIntField(long arrayPtr, long index, CFields field) {
+ return readIntField(getArrayElementPtr(arrayPtr, index, field.struct), field);
+ }
- @Specialization
- static byte readLong(long pointer, long offset) {
- assert offset >= 0;
- return UNSAFE.getByte(pointer + offset);
- }
+ public static void writeStructArrayIntField(long arrayPtr, long index, CFields field, int value) {
+ writeIntField(getArrayElementPtr(arrayPtr, index, field.struct), field, value);
+ }
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static byte readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib) {
- return readLong(asPointer(pointer, lib), offset);
- }
+ public static long readLongField(long structBasePtr, CFields field) {
+ assert field.type.isI64();
+ return NativeMemory.readLong(getFieldPtr(structBasePtr, field));
+ }
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static byte readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- return (byte) (int) call.call(NativeCAPISymbol.FUN_READ_CHAR_MEMBER, pointer, offset);
- }
+ public static void writeLongField(long structBasePtr, CFields field, long value) {
+ assert field.type.isI64();
+ NativeMemory.writeLong(getFieldPtr(structBasePtr, field), value);
+ }
- /**
- * Determines if the C type {@code char} is signed by looking at {@code CHAR_MIN}. If
- * {@code CHAR_MIN < 0}, then the type is signed.
- */
- protected static boolean isCharSigned() {
- return CConstants.CHAR_MIN.longValue() < 0;
- }
+ public static long readStructArrayLongField(long arrayPtr, long index, CFields field) {
+ return readLongField(getArrayElementPtr(arrayPtr, index, field.struct), field);
+ }
- public static ReadByteNode getUncached() {
- return CStructAccessFactory.ReadByteNodeGen.getUncached();
- }
+ public static void writeStructArrayLongField(long arrayPtr, long index, CFields field, long value) {
+ writeLongField(getArrayElementPtr(arrayPtr, index, field.struct), field, value);
}
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class ReadI16Node extends ReadBaseNode {
+ public static double readDoubleField(long structBasePtr, CFields field) {
+ assert field.type.isDouble();
+ return NativeMemory.readDouble(getFieldPtr(structBasePtr, field));
+ }
- abstract int execute(Object pointer, long offset);
+ public static void writeDoubleField(long structBasePtr, CFields field, double value) {
+ assert field.type.isDouble();
+ NativeMemory.writeDouble(getFieldPtr(structBasePtr, field), value);
+ }
- public final int read(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
+ public static long readPtrField(long structBasePtr, CFields field) {
+ assert field.type.isPyObjectOrPointer();
+ return NativeMemory.readLong(getFieldPtr(structBasePtr, field));
+ }
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI16();
- }
+ public static void writePtrField(long structBasePtr, CFields field, long value) {
+ assert field.type.isPyObjectOrPointer();
+ NativeMemory.writeLong(getFieldPtr(structBasePtr, field), value);
+ }
- public final int readOffset(Object pointer, long offset) {
- return execute(pointer, offset);
- }
+ public static long readStructArrayPtrField(long arrayPtr, long index, CFields field) {
+ return readPtrField(getArrayElementPtr(arrayPtr, index, field.struct), field);
+ }
- public final int readArrayElement(Object pointer, long element) {
- return execute(pointer, element * Short.BYTES);
- }
+ public static void writeStructArrayPtrField(long arrayPtr, long index, CFields field, long value) {
+ writePtrField(getArrayElementPtr(arrayPtr, index, field.struct), field, value);
+ }
- @Specialization
- static int readLong(long pointer, long offset) {
- assert offset >= 0;
- return UNSAFE.getShort(pointer + offset);
- }
+ private static long getArrayElementPtr(long arrayPtr, long index, CStructs struct) {
+ return arrayPtr + index * struct.size();
+ }
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static int readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib) {
- return readLong(asPointer(pointer, lib), offset);
+ public static long allocatePyMem(long count, long elsize) {
+ assert elsize >= 0;
+ try {
+ return ExternalFunctionInvoker.invokePYMEM_ALLOC(
+ CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_PYMEM_ALLOC).getAddress(), count, elsize);
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
}
+ }
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static int readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- return (int) call.call(NativeCAPISymbol.FUN_READ_SHORT_MEMBER, pointer, offset);
- }
+ public static long allocatePyMem(int size) {
+ return allocatePyMem(1, size);
}
@ImportStatic(PGuards.class)
@GenerateUncached
@GenerateInline(false)
- public abstract static class ReadI32Node extends ReadBaseNode {
+ public abstract static class ReadObjectNode extends Node {
+ abstract Object execute(long pointer, long offset);
- public static int readUncached(Object pointer, CFields field) {
- return ReadI32NodeGen.getUncached().read(pointer, field);
- }
-
- abstract int execute(Object pointer, long offset);
-
- public final int read(Object pointer, CFields field) {
- assert accepts(field);
+ public final Object read(long pointer, CFields field) {
+ assert field.type.isPyObject();
return execute(pointer, field.offset());
}
- public final int readFromObj(PythonNativeObject self, CFields field) {
- return read(self.getPtr(), field);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI32();
- }
-
- public final int readOffset(Object pointer, long offset) {
+ public final Object read(long pointer, long offset) {
return execute(pointer, offset);
}
- public final int readArrayElement(Object pointer, long element) {
- return execute(pointer, element * Integer.BYTES);
- }
-
- public final int readStructArrayElement(Object pointer, long element, CFields field) {
- assert accepts(field);
- return execute(pointer, element * field.struct.size() + field.offset());
- }
-
- @Specialization
- static int readLong(long pointer, long offset) {
- assert offset >= 0;
- return UNSAFE.getInt(pointer + offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static int readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib) {
- return readLong(asPointer(pointer, lib), offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static int readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- return (int) call.call(NativeCAPISymbol.FUN_READ_INT_MEMBER, pointer, offset);
- }
-
- public static ReadI32Node getUncached() {
- return ReadI32NodeGen.getUncached();
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class ReadI64Node extends ReadBaseNode {
-
- abstract long execute(Object pointer, long offset);
-
- public final long read(Object pointer) {
- return execute(pointer, 0);
- }
-
- public final long read(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
-
- public final long readFromObj(PythonNativeObject self, CFields field) {
+ public final Object readFromObj(PythonNativeObject self, CFields field) {
return read(self.getPtr(), field);
}
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI64();
- }
-
- public final long[] readLongArray(Object pointer, int elements) {
- return readLongArray(pointer, elements, 0);
+ public final Object[] readPyObjectArray(long pointer, int elements) {
+ return readPyObjectArray(pointer, elements, 0);
}
- public final long[] readLongArray(Object pointer, int elements, int offset) {
- long[] result = new long[elements];
+ public final Object[] readPyObjectArray(long pointer, int elements, int offset) {
+ Object[] result = new Object[elements];
for (int i = 0; i < result.length; i++) {
result[i] = execute(pointer, (i + offset) * POINTER_SIZE);
}
return result;
}
- public final int[] readLongAsIntArray(Object pointer, int elements) {
- return readLongAsIntArray(pointer, elements, 0);
- }
-
- public final int[] readLongAsIntArray(Object pointer, int elements, int offset) {
- int[] result = new int[elements];
- for (int i = 0; i < result.length; i++) {
- result[i] = (int) execute(pointer, (i + offset) * POINTER_SIZE);
- }
- return result;
- }
-
- public final long readArrayElement(Object pointer, long element) {
- return execute(pointer, element * POINTER_SIZE);
- }
-
- public final long readStructArrayElement(Object pointer, long element, CFields field) {
- assert accepts(field);
- return execute(pointer, element * field.struct.size() + field.offset());
- }
-
@Specialization
- static long readLong(long pointer, long offset) {
+ static Object readLong(long pointer, long offset,
+ @Cached NativePtrToPythonNode toPython) {
assert offset >= 0;
- return UNSAFE.getLong(pointer + offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static long readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib) {
- return readLong(asPointer(pointer, lib), offset);
+ return toPython.execute(NativeMemory.readPtr(pointer + offset), false);
}
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static long readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @CachedLibrary(limit = "3") InteropLibrary resultLib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- Object result = call.call(NativeCAPISymbol.FUN_READ_LONG_MEMBER, pointer, offset);
- if (result instanceof Long) {
- return (long) result;
- }
- try {
- return resultLib.asLong(result);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- }
-
- public static ReadI64Node getUncached() {
- return CStructAccessFactory.ReadI64NodeGen.getUncached();
- }
-
- @NeverDefault
- public static ReadI64Node create() {
- return CStructAccessFactory.ReadI64NodeGen.create();
+ public static ReadObjectNode getUncached() {
+ return ReadObjectNodeGen.getUncached();
}
}
- /**
- * Note that this node returns a double, not a float, even though it reads only 32 bits.
- */
@ImportStatic(PGuards.class)
@GenerateUncached
@GenerateInline(false)
- public abstract static class ReadFloatNode extends ReadBaseNode {
+ public abstract static class ReadCharPtrNode extends Node {
+ abstract TruffleString execute(long pointer, CFields field);
- abstract double execute(Object pointer, long offset);
-
- public final double read(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
-
- public final double readFromObj(PythonNativeObject self, CFields field) {
- return read(self.getPtr(), field);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isDouble();
+ public static TruffleString executeUncached(long pointer, CFields field) {
+ return ReadCharPtrNodeGen.getUncached().execute(pointer, field);
}
- public final double readArrayElement(Object pointer, int element) {
- return execute(pointer, element * Float.BYTES);
+ public final TruffleString readFromObj(PythonNativeObject self, CFields field) {
+ return execute(self.getPtr(), field);
}
@Specialization
- static double readLong(long pointer, long offset) {
- assert offset >= 0;
- return UNSAFE.getFloat(pointer + offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static double readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib) {
- return readLong(asPointer(pointer, lib), offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static double readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @CachedLibrary(limit = "3") InteropLibrary resultLib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- Object result = call.call(NativeCAPISymbol.FUN_READ_FLOAT_MEMBER, pointer, offset);
- if (result instanceof Double) {
- return (double) result;
- }
- try {
- return resultLib.asFloat(result);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- }
-
- public static ReadFloatNode getUncached() {
- return CStructAccessFactory.ReadFloatNodeGen.getUncached();
- }
-
- @NeverDefault
- public static ReadFloatNode create() {
- return CStructAccessFactory.ReadFloatNodeGen.create();
+ static TruffleString readLong(long pointer, CFields field,
+ @Bind Node inliningTarget,
+ @Cached FromCharPointerNode toPython) {
+ assert field.type.isCharPtr();
+ return toPython.execute(inliningTarget, readPtrField(pointer, field));
}
}
- @ImportStatic(PGuards.class)
@GenerateUncached
@GenerateInline(false)
- public abstract static class ReadDoubleNode extends ReadBaseNode {
-
- abstract double execute(Object pointer, long offset);
+ public abstract static class WriteTruffleStringNode extends Node {
- public final double read(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
+ abstract void execute(long dstPointer, int dstOffset, TruffleString src, int srcOffset, int length, TruffleString.Encoding encoding);
- public final double readFromObj(PythonNativeObject self, CFields field) {
- return read(self.getPtr(), field);
+ @TruffleBoundary
+ public static void executeUncached(long dstPointer, int dstOffset, TruffleString src, int srcOffset, int length, TruffleString.Encoding encoding) {
+ WriteTruffleStringNodeGen.getUncached().execute(dstPointer, dstOffset, src, srcOffset, length, encoding);
}
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isDouble();
+ public final void write(long dstPointer, TruffleString src, TruffleString.Encoding encoding) {
+ execute(dstPointer, 0, src, 0, src.byteLength(encoding), encoding);
}
- public final double readArrayElement(Object pointer, int element) {
- return execute(pointer, element * Double.BYTES);
+ @TruffleBoundary
+ public static void writeUncached(long dstPointer, TruffleString src, TruffleString.Encoding encoding) {
+ executeUncached(dstPointer, 0, src, 0, src.byteLength(encoding), encoding);
}
@Specialization
- static double readLong(long pointer, long offset) {
- assert offset >= 0;
- return UNSAFE.getDouble(pointer + offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static double readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib) {
- return readLong(asPointer(pointer, lib), offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static double readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @CachedLibrary(limit = "3") InteropLibrary resultLib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- Object result = call.call(NativeCAPISymbol.FUN_READ_DOUBLE_MEMBER, pointer, offset);
- if (result instanceof Double) {
- return (double) result;
- }
- try {
- return resultLib.asDouble(result);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- }
-
- public static ReadDoubleNode getUncached() {
- return CStructAccessFactory.ReadDoubleNodeGen.getUncached();
- }
-
- @NeverDefault
- public static ReadDoubleNode create() {
- return CStructAccessFactory.ReadDoubleNodeGen.create();
+ static void writeLong(long dstPointer, int dstOffset, TruffleString src, int srcOffset, int length, TruffleString.Encoding encoding,
+ @Cached TruffleString.CopyToNativeMemoryNode copyToNativeMemoryNode) {
+ copyToNativeMemoryNode.execute(src, srcOffset, dstPointer, dstOffset, length, encoding);
}
}
@ImportStatic(PGuards.class)
@GenerateUncached
@GenerateInline(false)
- public abstract static class ReadPointerNode extends ReadBaseNode {
-
- abstract Object execute(Object pointer, long offset);
+ public abstract static class WriteObjectNewRefNode extends Node {
- public final Object read(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
-
- public static Object readUncached(Object pointer, CFields field) {
- return getUncached().read(pointer, field);
- }
+ abstract void execute(long pointer, long offset, Object value);
- public final Object readFromObj(PythonNativeObject self, CFields field) {
- return read(self.getPtr(), field);
+ public final void write(long pointer, CFields field, Object value) {
+ assert field.type.isPyObject();
+ execute(pointer, field.offset(), value);
}
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isPyObjectOrPointer();
+ public final void writeToObject(PythonNativeObject self, CFields field, Object value) {
+ write(self.getPtr(), field, value);
}
- public final Object readArrayElement(Object pointer, long element) {
- return execute(pointer, element * POINTER_SIZE);
+ public final void write(long pointer, Object value) {
+ execute(pointer, 0, value);
}
- public final Object readStructArrayElement(Object pointer, long element, CFields field) {
- assert accepts(field);
- return execute(pointer, element * field.struct.size() + field.offset());
+ public final void writeArray(long pointer, Object[] values, int length, int sourceOffset, long targetOffset) {
+ if (length > values.length) {
+ throw shouldNotReachHere();
+ }
+ for (int i = 0; i < length; i++) {
+ execute(pointer, (i + targetOffset) * POINTER_SIZE, values[i + sourceOffset]);
+ }
}
@Specialization
- static Object readLong(long pointer, long offset) {
+ static void writeLong(long pointer, long offset, Object value,
+ @Cached NativePtrToPythonNode toPython,
+ @Cached PythonToNativeNewRefNode toNative) {
assert offset >= 0;
- return new NativePointer(UNSAFE.getLong(pointer + offset));
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static Object readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib) {
- return readLong(asPointer(pointer, lib), offset);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static Object readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- return call.call(NativeCAPISymbol.FUN_READ_POINTER_MEMBER, pointer, offset);
- }
-
- public static ReadPointerNode getUncached() {
- return ReadPointerNodeGen.getUncached();
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class ReadObjectNode extends ReadBaseNode {
- abstract Object execute(Object pointer, long offset);
-
- public final Object read(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
-
- public final Object readFromObj(PythonNativeObject self, CFields field) {
- return read(self.getPtr(), field);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isPyObject();
- }
-
- public final Object readArrayElement(Object pointer, long element) {
- return execute(pointer, element * POINTER_SIZE);
- }
-
- public final Object[] readPyObjectArray(Object pointer, int elements) {
- return readPyObjectArray(pointer, elements, 0);
- }
-
- public final Object[] readPyObjectArray(Object pointer, int elements, int offset) {
- Object[] result = new Object[elements];
- for (int i = 0; i < result.length; i++) {
- result[i] = execute(pointer, (i + offset) * POINTER_SIZE);
- }
- return result;
- }
-
- @Specialization
- static Object readLong(long pointer, long offset,
- @Shared @Cached NativePtrToPythonNode toPython) {
- assert offset >= 0;
- return toPython.execute(UNSAFE.getLong(pointer + offset), false);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static Object readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib,
- @Shared @Cached NativePtrToPythonNode toPython) {
- return readLong(asPointer(pointer, lib), offset, toPython);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static Object readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call,
- @Cached NativeToPythonNode toPython) {
- assert validPointer(pointer);
- return toPython.execute(call.call(NativeCAPISymbol.FUN_READ_POINTER_MEMBER, pointer, offset));
- }
-
- public static ReadObjectNode getUncached() {
- return ReadObjectNodeGen.getUncached();
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class ReadCharPtrNode extends ReadBaseNode {
- abstract TruffleString execute(Object pointer, long offset);
-
- public final TruffleString read(Object pointer, CFields field) {
- assert accepts(field);
- return execute(pointer, field.offset());
- }
-
- public final TruffleString readFromObj(PythonNativeObject self, CFields field) {
- return read(self.getPtr(), field);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isCharPtr();
- }
-
- public final TruffleString readArrayElement(Object pointer, long element) {
- return execute(pointer, element * POINTER_SIZE);
- }
-
- public final TruffleString readStructArrayElement(Object pointer, long element, CFields field) {
- assert accepts(field);
- return execute(pointer, element * field.struct.size() + field.offset());
- }
-
- @Specialization
- static TruffleString readLong(long pointer, long offset,
- @Shared @Cached FromCharPointerNode toPython) {
- assert offset >= 0;
- return toPython.execute(UNSAFE.getLong(pointer + offset));
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static TruffleString readPointer(Object pointer, long offset,
- @CachedLibrary("pointer") InteropLibrary lib,
- @Shared @Cached FromCharPointerNode toPython) {
- return readLong(asPointer(pointer, lib), offset, toPython);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static TruffleString readManaged(Object pointer, long offset,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call,
- @Shared @Cached FromCharPointerNode toPython) {
- assert validPointer(pointer);
- return toPython.execute(call.call(NativeCAPISymbol.FUN_READ_POINTER_MEMBER, pointer, offset));
- }
-
- public static ReadCharPtrNode getUncached() {
- return ReadCharPtrNodeGen.getUncached();
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WriteByteNode extends Node implements CStructAccessNode {
-
- abstract void execute(Object pointer, long offset, byte value);
-
- public final void write(Object pointer, CFields field, byte value) {
- assert accepts(field);
- execute(pointer, field.offset(), value);
- }
-
- public final void write(Object pointer, byte value) {
- execute(pointer, 0, value);
- }
-
- public final void writeToObject(PythonNativeObject self, CFields field, byte value) {
- write(self.getPtr(), field, value);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI8();
- }
-
- public final void writeByteArray(Object pointer, byte[] values) {
- writeByteArray(pointer, values, values.length, 0, 0);
- }
-
- public final void writeByteArray(Object pointer, byte[] values, int length, int sourceOffset, int targetOffset) {
- for (int i = 0; i < length; i++) {
- execute(pointer, (i + targetOffset) * Byte.BYTES, values[i + sourceOffset]);
- }
- }
-
- public final void writeArrayElement(Object pointer, long element, byte value) {
- execute(pointer, element * Byte.BYTES, value);
- }
-
- @Specialization
- static void writeLong(long pointer, long offset, byte value) {
- assert offset >= 0;
- UNSAFE.putByte(pointer + offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static void writePointer(Object pointer, long offset, byte value,
- @CachedLibrary("pointer") InteropLibrary lib) {
- writeLong(asPointer(pointer, lib), offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static void writeManaged(Object pointer, long offset, byte value,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- call.call(NativeCAPISymbol.FUN_WRITE_CHAR_MEMBER, pointer, offset, value);
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WriteDoubleNode extends Node implements CStructAccessNode {
-
- abstract void execute(Object pointer, long offset, double value);
-
- public final void write(Object pointer, CFields field, double value) {
- assert accepts(field);
- execute(pointer, field.offset(), value);
- }
-
- public final void write(Object pointer, double value) {
- execute(pointer, 0, value);
- }
-
- public final void writeArrayElement(Object pointer, long element, double value) {
- execute(pointer, element * Double.BYTES, value);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isDouble();
- }
-
- @Specialization
- static void writeLong(long pointer, long offset, double value) {
- assert offset >= 0;
- UNSAFE.putDouble(pointer + offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static void writePointer(Object pointer, long offset, double value,
- @CachedLibrary("pointer") InteropLibrary lib) {
- writeLong(asPointer(pointer, lib), offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static void writeManaged(Object pointer, long offset, double value,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- call.call(NativeCAPISymbol.FUN_WRITE_DOUBLE_MEMBER, pointer, offset, value);
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WriteFloatNode extends Node implements CStructAccessNode {
-
- abstract void execute(Object pointer, long offset, float value);
-
- public final void write(Object pointer, CFields field, float value) {
- assert accepts(field);
- execute(pointer, field.offset(), value);
- }
-
- public final void write(Object pointer, float value) {
- execute(pointer, 0, value);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isFloat();
- }
-
- @Specialization
- static void writeLong(long pointer, long offset, float value) {
- assert offset >= 0;
- UNSAFE.putFloat(pointer + offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static void writePointer(Object pointer, long offset, float value,
- @CachedLibrary("pointer") InteropLibrary lib) {
- writeLong(asPointer(pointer, lib), offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static void writeManaged(Object pointer, long offset, float value,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- call.call(NativeCAPISymbol.FUN_WRITE_FLOAT_MEMBER, pointer, offset, value);
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WriteI16Node extends Node implements CStructAccessNode {
-
- abstract void execute(Object pointer, long offset, short value);
-
- public final void write(Object pointer, CFields field, short value) {
- assert accepts(field);
- execute(pointer, field.offset(), value);
- }
-
- public final void write(Object pointer, short value) {
- execute(pointer, 0, value);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI16();
- }
-
- @Specialization
- static void writeLong(long pointer, long offset, short value) {
- assert offset >= 0;
- UNSAFE.putShort(pointer + offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static void writePointer(Object pointer, long offset, short value,
- @CachedLibrary("pointer") InteropLibrary lib) {
- writeLong(asPointer(pointer, lib), offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static void writeManaged(Object pointer, long offset, short value,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- call.call(NativeCAPISymbol.FUN_WRITE_SHORT_MEMBER, pointer, offset, value);
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WriteIntNode extends Node implements CStructAccessNode {
-
- public static void writeUncached(Object pointer, CFields field, int value) {
- CStructAccessFactory.WriteIntNodeGen.getUncached().write(pointer, field, value);
- }
-
- abstract void execute(Object pointer, long offset, int value);
-
- public final void write(Object pointer, CFields field, int value) {
- assert accepts(field);
- execute(pointer, field.offset(), value);
- }
-
- public final void write(Object pointer, int value) {
- execute(pointer, 0, value);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI32();
- }
-
- public final void writeArray(Object pointer, int[] values) {
- writeArray(pointer, values, values.length, 0, 0);
- }
-
- public final void writeArray(Object pointer, int[] values, int length, int sourceOffset, long targetOffset) {
- for (int i = 0; i < length; i++) {
- execute(pointer, (i + targetOffset) * Integer.BYTES, values[i + sourceOffset]);
- }
- }
-
- public final void writeArrayElement(Object pointer, long element, int value) {
- execute(pointer, element * Integer.BYTES, value);
- }
-
- public final void writeStructArrayElement(Object pointer, long element, CFields field, int value) {
- execute(pointer, element * field.struct.size() + field.offset(), value);
- }
-
- @Specialization
- static void writeLong(long pointer, long offset, int value) {
- assert offset >= 0;
- UNSAFE.putInt(pointer + offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static void writePointer(Object pointer, long offset, int value,
- @CachedLibrary("pointer") InteropLibrary lib) {
- writeLong(asPointer(pointer, lib), offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static void writeManaged(Object pointer, long offset, int value,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- call.call(NativeCAPISymbol.FUN_WRITE_INT_MEMBER, pointer, offset, value);
- }
-
- public static WriteIntNode getUncached() {
- return WriteIntNodeGen.getUncached();
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WriteLongNode extends Node implements CStructAccessNode {
-
- abstract void execute(Object pointer, long offset, long value);
-
- public final void write(Object pointer, CFields field, long value) {
- assert accepts(field);
- execute(pointer, field.offset(), value);
- }
-
- public final void writeToObject(PythonNativeObject self, CFields field, long value) {
- write(self.getPtr(), field, value);
- }
-
- public final void write(Object pointer, long value) {
- execute(pointer, 0, value);
- }
-
- public final void writeIntArray(Object pointer, int[] values) {
- writeIntArray(pointer, values, values.length, 0, 0);
- }
-
- public final void writeIntArray(Object pointer, int[] values, int length, int sourceOffset, long targetOffset) {
- for (int i = 0; i < length; i++) {
- execute(pointer, (i + targetOffset) * Long.BYTES, values[i + sourceOffset]);
- }
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI64();
- }
-
- @Specialization
- public static void writeLong(long pointer, long offset, long value) {
- assert offset >= 0;
- UNSAFE.putLong(pointer + offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static void writePointer(Object pointer, long offset, long value,
- @CachedLibrary("pointer") InteropLibrary lib) {
- writeLong(asPointer(pointer, lib), offset, value);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static void writeManaged(Object pointer, long offset, long value,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- call.call(NativeCAPISymbol.FUN_WRITE_LONG_MEMBER, pointer, offset, value);
- }
-
- public static WriteLongNode getUncached() {
- return WriteLongNodeGen.getUncached();
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WriteTruffleStringNode extends Node implements CStructAccessNode {
-
- abstract void execute(Object dstPointer, int dstOffset, TruffleString src, int srcOffset, int length, TruffleString.Encoding encoding);
-
- public final void write(Object dstPointer, TruffleString src, TruffleString.Encoding encoding) {
- execute(dstPointer, 0, src, 0, src.byteLength(encoding), encoding);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isI8();
- }
-
- @Specialization
- static void writeLong(long dstPointer, int dstOffset, TruffleString src, int srcOffset, int length, TruffleString.Encoding encoding,
- @Cached @Shared TruffleString.CopyToNativeMemoryNode copyToNativeMemoryNode) {
- copyToNativeMemoryNode.execute(src, srcOffset, new NativePointer(dstPointer), dstOffset, length, encoding);
- }
-
- @Specialization(guards = {"!isLong(dstPointer)", "lib.isPointer(dstPointer)"}, limit = "3")
- static void writePointer(Object dstPointer, int dstOffset, TruffleString src, int srcOffset, int length, TruffleString.Encoding encoding,
- @SuppressWarnings("unused") @CachedLibrary("dstPointer") InteropLibrary lib,
- @Cached @Shared TruffleString.CopyToNativeMemoryNode copyToNativeMemoryNode) {
- copyToNativeMemoryNode.execute(src, srcOffset, dstPointer, dstOffset, length, encoding);
- }
-
- @Specialization(guards = {"!isLong(dstPointer)", "!lib.isPointer(dstPointer)"})
- static void writeManaged(Object dstPointer, int dstOffset, TruffleString src, int srcOffset, int length, TruffleString.Encoding encoding,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call,
- @Cached TruffleString.ReadByteNode readByteNode) {
- assert validPointer(dstPointer);
- for (int i = 0; i < length; i++) {
- call.call(NativeCAPISymbol.FUN_WRITE_CHAR_MEMBER, dstPointer, dstOffset + i, readByteNode.execute(src, srcOffset + i, encoding));
- }
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WritePointerNode extends Node implements CStructAccessNode {
-
- public static void writeUncached(Object pointer, CFields field, Object value) {
- WritePointerNodeGen.getUncached().write(pointer, field, value);
- }
-
- public static void writeUncached(Object pointer, long offset, Object value) {
- WritePointerNodeGen.getUncached().execute(pointer, offset, value);
- }
-
- public static void writeArrayElementUncached(long pointer, long element, long value) {
- UNSAFE.putLong(pointer + element * POINTER_SIZE, value);
- }
-
- abstract void execute(Object pointer, long offset, Object value);
-
- public final void write(Object pointer, CFields field, Object value) {
- assert accepts(field);
- execute(pointer, field.offset(), value);
- }
-
- public final void writeToObj(PythonAbstractNativeObject obj, CFields field, Object value) {
- write(obj.getPtr(), field, value);
- }
-
- public final void write(Object pointer, Object value) {
- execute(pointer, 0, value);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isPyObjectOrPointer();
- }
-
- public final void writeArrayElement(Object pointer, long element, Object value) {
- execute(pointer, element * POINTER_SIZE, value);
- }
-
- public final void writePointerArray(Object pointer, long[] values, int length, int sourceOffset, long targetOffset) {
- for (int i = 0; i < length; i++) {
- execute(pointer, (i + targetOffset) * POINTER_SIZE, values[i + sourceOffset]);
- }
- }
-
- @Specialization
- static void writeLong(long pointer, long offset, Object value,
- @Bind Node inliningTarget,
- @Shared @Cached CoerceNativePointerToLongNode coerceToLongNode) {
- assert offset >= 0;
- UNSAFE.putLong(pointer + offset, coerceToLongNode.execute(inliningTarget, value));
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"}, limit = "3")
- static void writePointer(Object pointer, long offset, Object value,
- @Bind Node inliningTarget,
- @CachedLibrary("pointer") InteropLibrary lib,
- @Shared @Cached CoerceNativePointerToLongNode coerceToLongNode) {
- writeLong(asPointer(pointer, lib), offset, value, inliningTarget, coerceToLongNode);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static void writeManaged(Object pointer, long offset, Object value,
- @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- call.call(NativeCAPISymbol.FUN_WRITE_POINTER_MEMBER, pointer, offset, value);
- }
-
- public static WritePointerNode getUncached() {
- return WritePointerNodeGen.getUncached();
- }
- }
-
- @ImportStatic(PGuards.class)
- @GenerateUncached
- @GenerateInline(false)
- public abstract static class WriteObjectNewRefNode extends Node implements CStructAccessNode {
-
- abstract void execute(Object pointer, long offset, Object value);
-
- public final void write(Object pointer, CFields field, Object value) {
- assert accepts(field);
- execute(pointer, field.offset(), value);
- }
-
- public final void writeToObject(PythonNativeObject self, CFields field, Object value) {
- write(self.getPtr(), field, value);
- }
-
- public final void write(Object pointer, Object value) {
- execute(pointer, 0, value);
- }
-
- public final boolean accepts(ArgDescriptor desc) {
- return desc.isPyObject();
- }
-
- public final void writeArray(Object pointer, Object[] values, int length, int sourceOffset, long targetOffset) {
- if (length > values.length) {
- throw CompilerDirectives.shouldNotReachHere();
- }
- for (int i = 0; i < length; i++) {
- execute(pointer, (i + targetOffset) * POINTER_SIZE, values[i + sourceOffset]);
- }
- }
-
- public final void writeArrayElement(Object pointer, long element, Object value) {
- execute(pointer, element * POINTER_SIZE, value);
- }
-
- @Specialization
- static void writeLong(long pointer, long offset, Object value,
- @Bind Node inliningTarget,
- @Shared @Cached NativePtrToPythonNode toPython,
- @Shared @Cached PythonToNativeNewRefNode toNative,
- @Shared @Cached CoerceNativePointerToLongNode coerceToLongNode) {
- assert offset >= 0;
- long old = UNSAFE.getLong(pointer + offset);
- if (old != 0) {
+ long old = NativeMemory.readPtr(pointer + offset);
+ if (old != NULLPTR) {
toPython.execute(old, true);
}
- long lvalue = coerceToLongNode.execute(inliningTarget, toNative.execute(value));
- UNSAFE.putLong(pointer + offset, lvalue);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "lib.isPointer(pointer)"})
- static void writePointer(Object pointer, long offset, Object value,
- @Bind Node inliningTarget,
- @Shared @Cached NativePtrToPythonNode toPython,
- @Shared @Cached PythonToNativeNewRefNode toNative,
- @Shared @CachedLibrary(limit = "3") InteropLibrary lib,
- @Shared @Cached CoerceNativePointerToLongNode coerceToLongNode) {
- writeLong(asPointer(pointer, lib), offset, value, inliningTarget, toPython, toNative, coerceToLongNode);
- }
-
- @Specialization(guards = {"!isLong(pointer)", "!lib.isPointer(pointer)"})
- static void writeManaged(Object pointer, long offset, Object value,
- @Shared @SuppressWarnings("unused") @CachedLibrary(limit = "3") InteropLibrary lib,
- @Cached PythonToNativeNode toNative,
- @Cached PCallCapiFunction call) {
- assert validPointer(pointer);
- call.call(NativeCAPISymbol.FUN_WRITE_OBJECT_MEMBER, pointer, offset, toNative.execute(value));
- }
- }
-
- private interface CStructAccessNode {
- boolean accepts(ArgDescriptor desc);
-
- default boolean accepts(CFields field) {
- return accepts(field.type);
+ NativeMemory.writePtr(pointer + offset, toNative.executeLong(value));
}
}
-
- public static final long POINTER_SIZE = 8;
- private static final Unsafe UNSAFE = PythonUtils.initUnsafe();
-
- static long asPointer(Object value, InteropLibrary lib) {
- assert validPointer(value) || value instanceof PySequenceArrayWrapper;
- try {
- return lib.asPointer(value);
- } catch (final UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
-
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructs.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructs.java
index 0bdf9983a6..efaa969e25 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructs.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/structs/CStructs.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2023, 2026, 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
@@ -40,8 +40,11 @@
*/
package com.oracle.graal.python.builtins.objects.cext.structs;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readLongArrayElements;
+
import com.oracle.graal.python.annotations.CApiStructs;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
@@ -89,6 +92,7 @@ public enum CStructs {
PyGetSetDef,
PyMemberDef,
PyThreadState,
+ GraalPyDeallocState,
wchar_t,
long__long,
Py_ssize_t,
@@ -113,8 +117,14 @@ public int size() {
private static void resolve() {
CompilerAsserts.neverPartOfCompilation();
- Object sizesPointer = PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_PYTRUFFLE_STRUCT_SIZES);
- long[] sizes = CStructAccessFactory.ReadI64NodeGen.getUncached().readLongArray(sizesPointer, VALUES.length);
+ long sizesPointer;
+ try {
+ sizesPointer = ExternalFunctionInvoker.invokePYTRUFFLE_STRUCT_SIZES(
+ CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_PYTRUFFLE_STRUCT_SIZES).getAddress());
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
+ long[] sizes = readLongArrayElements(sizesPointer, 0L, VALUES.length);
for (CStructs struct : VALUES) {
long size = sizes[struct.ordinal()];
assert size > 0 && size < 1024;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeNodes.java
index c7545b8d6d..ac5e582732 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/code/CodeNodes.java
@@ -82,6 +82,20 @@ public abstract class CodeNodes {
public static class CreateCodeNode extends PNodeWithContext {
@Child private BoundaryCallData boundaryCallData = BoundaryCallData.createFor(this);
+ @TruffleBoundary
+ public static PCode executeUncached(int argcount,
+ int posonlyargcount, int kwonlyargcount,
+ int nlocals, int stacksize, int flags,
+ byte[] codedata, Object[] constants, TruffleString[] names,
+ TruffleString[] varnames, TruffleString[] freevars, TruffleString[] cellvars,
+ TruffleString filename, TruffleString name, TruffleString qualname, int firstlineno,
+ byte[] linetable) {
+ return executeInternal(null, null, BoundaryCallData.getUncached(),
+ argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, codedata,
+ constants, names, varnames, freevars, cellvars,
+ filename, name, qualname, firstlineno, linetable);
+ }
+
public PCode execute(VirtualFrame frame, int argcount,
int posonlyargcount, int kwonlyargcount,
int nlocals, int stacksize, int flags,
@@ -89,9 +103,21 @@ public PCode execute(VirtualFrame frame, int argcount,
TruffleString[] varnames, TruffleString[] freevars, TruffleString[] cellvars,
TruffleString filename, TruffleString name, TruffleString qualname, int firstlineno,
byte[] linetable) {
+ return executeInternal(frame, this, boundaryCallData,
+ argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize, flags, codedata,
+ constants, names, varnames, freevars, cellvars,
+ filename, name, qualname, firstlineno, linetable);
+ }
- PythonContext context = PythonContext.get(this);
- PythonLanguage language = context.getLanguage(this);
+ private static PCode executeInternal(VirtualFrame frame, Node node, BoundaryCallData boundaryCallData,
+ int argcount, int posonlyargcount, int kwonlyargcount,
+ int nlocals, int stacksize, int flags,
+ byte[] codedata, Object[] constants, TruffleString[] names,
+ TruffleString[] varnames, TruffleString[] freevars, TruffleString[] cellvars,
+ TruffleString filename, TruffleString name, TruffleString qualname, int firstlineno,
+ byte[] linetable) {
+ PythonContext context = PythonContext.get(node);
+ PythonLanguage language = context.getLanguage(node);
Object state = BoundaryCallContext.enter(frame, language, context, boundaryCallData);
try {
return createCode(language, argcount,
@@ -188,6 +214,11 @@ public abstract static class GetCodeCallTargetNode extends PNodeWithContext {
public abstract RootCallTarget execute(Node inliningTarget, PCode code);
+ @TruffleBoundary
+ public static RootCallTarget executeUncached(PCode code) {
+ return CodeNodesFactory.GetCodeCallTargetNodeGen.getUncached().execute(null, code);
+ }
+
@Specialization(guards = {"cachedCode == code", "isSingleContext()"}, limit = "2")
static RootCallTarget doCachedCode(@SuppressWarnings("unused") PCode code,
@Cached(value = "code", weak = true) PCode cachedCode) {
@@ -206,6 +237,11 @@ static RootCallTarget doGeneric(PCode code) {
public abstract static class GetCodeSignatureNode extends PNodeWithContext {
public abstract Signature execute(Node inliningTarget, PCode code);
+ @TruffleBoundary
+ public static Signature executeUncached(PCode code) {
+ return CodeNodesFactory.GetCodeSignatureNodeGen.getUncached().execute(null, code);
+ }
+
@Specialization(guards = {"cachedCode == code", "isSingleContext(inliningTarget)"}, limit = "2")
static Signature doCached(Node inliningTarget, @SuppressWarnings("unused") PCode code,
@Cached("code") PCode cachedCode) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java
index 0bce7392c5..fb9f0b9239 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java
@@ -26,6 +26,17 @@
package com.oracle.graal.python.builtins.objects.common;
import static com.oracle.graal.python.builtins.objects.common.IndexNodes.checkBounds;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.callocByteArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.callocPtrArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.copyByteArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.copyPtrArray;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.free;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readByteArrayElement;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readPtrArrayElement;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElement;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElements;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writePtrArrayElement;
import static com.oracle.graal.python.runtime.exception.PythonErrorType.IndexError;
import static com.oracle.graal.python.runtime.exception.PythonErrorType.MemoryError;
import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError;
@@ -556,6 +567,11 @@ public final int executeIntCached(SequenceStorage s, int idx) throws UnexpectedR
return executeInt(null, s, idx);
}
+ @TruffleBoundary
+ public static int executeIntUncached(SequenceStorage s, int idx) throws UnexpectedResultException {
+ return GetItemScalarNodeGen.getUncached().executeInt(null, s, idx);
+ }
+
public final int executeKnownInt(Node inliningTarget, SequenceStorage s, int idx) {
try {
return executeInt(inliningTarget, s, idx);
@@ -641,15 +657,13 @@ protected abstract static class GetNativeItemScalarNode extends Node {
@Specialization
protected static Object doNativeObject(NativeObjectSequenceStorage storage, int idx,
- @Cached CStructAccess.ReadPointerNode readNode,
@Cached NativeToPythonNode toJavaNode) {
- return toJavaNode.execute(readNode.readArrayElement(storage.getPtr(), idx));
+ return toJavaNode.executeRaw(readPtrArrayElement(storage.getPtr(), idx));
}
@Specialization
- protected static int doNativeByte(NativeByteSequenceStorage storage, int idx,
- @Cached CStructAccess.ReadByteNode readNode) {
- return readNode.readArrayElement(storage.getPtr(), idx) & 0xff;
+ protected static int doNativeByte(NativeByteSequenceStorage storage, int idx) {
+ return readByteArrayElement(storage.getPtr(), idx) & 0xff;
}
}
@@ -793,23 +807,21 @@ protected static SequenceStorage doNativeInt(NativeIntSequenceStorage storage, i
}
@Specialization
- protected static SequenceStorage doNativeByte(NativeByteSequenceStorage storage, int start, @SuppressWarnings("unused") int stop, int step, int length,
- @Cached CStructAccess.ReadByteNode readNode) {
+ protected static SequenceStorage doNativeByte(NativeByteSequenceStorage storage, int start, @SuppressWarnings("unused") int stop, int step, int length) {
byte[] newArray = new byte[length];
for (int i = start, j = 0; j < length; i += step, j++) {
- newArray[j] = readNode.readArrayElement(storage.getPtr(), i);
+ newArray[j] = readByteArrayElement(storage.getPtr(), i);
}
return new ByteSequenceStorage(newArray);
}
@Specialization
protected static SequenceStorage doNativeObject(NativeObjectSequenceStorage storage, int start, @SuppressWarnings("unused") int stop, int step, int length,
- @Cached CStructAccess.ReadPointerNode readNode,
@Cached NativeToPythonNode toJavaNode) {
Object[] newArray = new Object[length];
for (int i = start, j = 0; j < length; i += step, j++) {
- newArray[j] = toJavaNode.execute(readNode.readArrayElement(storage.getPtr(), i));
+ newArray[j] = toJavaNode.executeRaw(readPtrArrayElement(storage.getPtr(), i));
}
return new ObjectSequenceStorage(newArray);
}
@@ -1253,7 +1265,7 @@ protected static void doIntLOvf(@SuppressWarnings("unused") Node inliningTarget,
}
@InliningCutoff
- @Specialization(guards = "!isNativeWrapper(value)")
+ @Specialization(guards = "!value.isNative()")
protected static void doInt(@SuppressWarnings("unused") Node inliningTarget, IntSequenceStorage storage, int idx, PInt value) {
try {
storage.setIntItemNormalized(idx, value.intValueExact());
@@ -1273,7 +1285,7 @@ protected static void doLong(@SuppressWarnings("unused") Node inliningTarget, Lo
}
@InliningCutoff
- @Specialization(guards = "!isNativeWrapper(value)")
+ @Specialization(guards = "!value.isNative()")
protected static void doLong(@SuppressWarnings("unused") Node inliningTarget, LongSequenceStorage storage, int idx, PInt value) {
try {
storage.setLongItemNormalized(idx, value.longValueExact());
@@ -1367,20 +1379,17 @@ public abstract static class SetNativeItemScalarNode extends Node {
@Specialization
protected static void doNativeByte(NativeByteSequenceStorage storage, int idx, Object value,
- @Cached CStructAccess.WriteByteNode writeNode,
@Cached CastToByteNode castToByteNode) {
- writeNode.writeArrayElement(storage.getPtr(), idx, castToByteNode.execute(null, value));
+ writeByteArrayElement(storage.getPtr(), idx, castToByteNode.execute(null, value));
}
@Specialization
protected static void doNativeObject(NativeObjectSequenceStorage storage, int idx, Object value,
@Bind Node inliningTarget,
@Cached PythonToNativeNewRefNode toNative,
- @Cached CStructAccess.ReadPointerNode readPointerNode,
- @Cached CStructAccess.WritePointerNode writePointerNode,
@Cached CExtNodes.XDecRefPointerNode decRefPointerNode) {
- Object old = readPointerNode.readArrayElement(storage.getPtr(), idx);
- writePointerNode.writeArrayElement(storage.getPtr(), idx, toNative.execute(value));
+ long old = readPtrArrayElement(storage.getPtr(), idx);
+ writePtrArrayElement(storage.getPtr(), idx, toNative.executeLong(value));
decRefPointerNode.execute(inliningTarget, old);
}
}
@@ -1393,16 +1402,14 @@ public abstract static class InitializeNativeItemScalarNode extends Node {
@Specialization
protected static void doNativeByte(NativeByteSequenceStorage storage, int idx, Object value,
- @Cached CStructAccess.WriteByteNode writeNode,
@Cached CastToByteNode castToByteNode) {
- writeNode.writeArrayElement(storage.getPtr(), idx, castToByteNode.execute(null, value));
+ writeByteArrayElement(storage.getPtr(), idx, castToByteNode.execute(null, value));
}
@Specialization
protected static void doNativeObject(NativeObjectSequenceStorage storage, int idx, Object value,
- @Cached CStructAccess.WritePointerNode writePointerNode,
@Cached PythonToNativeNewRefNode toNative) {
- writePointerNode.writeArrayElement(storage.getPtr(), idx, toNative.execute(value));
+ writePtrArrayElement(storage.getPtr(), idx, toNative.executeLong(value));
}
}
@@ -1635,39 +1642,35 @@ abstract static class ReverseNativeNode extends Node {
abstract void execute(NativeSequenceStorage storage);
@Specialization
- static void doNativeByte(NativeByteSequenceStorage storage,
- @Cached CStructAccess.ReadByteNode readByteNode,
- @Cached CStructAccess.WriteByteNode writeByteNode) {
+ static void doNativeByte(NativeByteSequenceStorage storage) {
int length = storage.length();
if (length > 0) {
int head = 0;
int tail = length - 1;
int middle = (length - 1) / 2;
- Object ptr = storage.getPtr();
+ long ptr = storage.getPtr();
for (; head <= middle; head++, tail--) {
- byte temp = readByteNode.readArrayElement(ptr, head);
- writeByteNode.writeArrayElement(ptr, head, readByteNode.readArrayElement(ptr, tail));
- writeByteNode.writeArrayElement(ptr, tail, temp);
+ byte temp = readByteArrayElement(ptr, head);
+ writeByteArrayElement(ptr, head, readByteArrayElement(ptr, tail));
+ writeByteArrayElement(ptr, tail, temp);
}
}
}
@Specialization
- static void doNativeObject(NativeObjectSequenceStorage storage,
- @Cached CStructAccess.ReadPointerNode readPointerNode,
- @Cached CStructAccess.WritePointerNode writePointerNode) {
+ static void doNativeObject(NativeObjectSequenceStorage storage) {
int length = storage.length();
if (length > 0) {
int head = 0;
int tail = length - 1;
int middle = (length - 1) / 2;
- Object ptr = storage.getPtr();
+ long ptr = storage.getPtr();
for (; head <= middle; head++, tail--) {
- Object temp = readPointerNode.readArrayElement(ptr, head);
- writePointerNode.writeArrayElement(ptr, head, readPointerNode.readArrayElement(ptr, tail));
- writePointerNode.writeArrayElement(ptr, tail, temp);
+ long temp = readPtrArrayElement(ptr, head);
+ writePtrArrayElement(ptr, head, readPtrArrayElement(ptr, tail));
+ writePtrArrayElement(ptr, tail, temp);
}
}
}
@@ -1855,21 +1858,18 @@ public final NativeSequenceStorage execute(Node inliningTarget, Object obj, int
}
@Specialization
- static NativeByteSequenceStorage doByte(byte[] arr, int length, boolean createRef,
- @Shared @Cached(inline = false) CStructAccess.AllocateNode alloc,
- @Cached(inline = false) CStructAccess.WriteByteNode write) {
- Object mem = alloc.calloc(arr.length + 1, java.lang.Byte.BYTES);
- write.writeByteArray(mem, arr);
+ static NativeByteSequenceStorage doByte(byte[] arr, int length, boolean createRef) {
+ long mem = callocByteArray(arr.length + 1L);
+ writeByteArrayElements(mem, 0, arr, 0, arr.length);
return NativeByteSequenceStorage.create(mem, length, arr.length, createRef);
}
@Specialization
static NativeSequenceStorage doObject(Object[] arr, int length, boolean createRef,
- @Shared @Cached(inline = false) CStructAccess.AllocateNode alloc,
@Cached(inline = false) CStructAccess.WriteObjectNewRefNode write) {
- Object mem = alloc.calloc(arr.length + 1, CStructAccess.POINTER_SIZE);
- write.writeArray(mem, arr, length, 0, 0);
- return NativeObjectSequenceStorage.create(mem, length, arr.length, createRef);
+ long memPtr = callocPtrArray(arr.length + 1L);
+ write.writeArray(memPtr, arr, length, 0, 0);
+ return NativeObjectSequenceStorage.create(memPtr, length, arr.length, createRef);
}
}
@@ -3213,55 +3213,28 @@ abstract static class EnsureCapacityNativeNode extends Node {
abstract void execute(NativeSequenceStorage s, int cap);
@Specialization
- static void doNativeByte(NativeByteSequenceStorage s, int cap,
- @Bind Node inliningTarget,
- @Shared @CachedLibrary(limit = "2") InteropLibrary lib,
- @Shared @Cached CStructAccess.AllocateNode alloc,
- @Shared @Cached CStructAccess.FreeNode free,
- @Shared @Cached PRaiseNode raiseNode,
- @Cached CStructAccess.ReadByteNode read,
- @Cached CStructAccess.WriteByteNode write) {
+ static void doNativeByte(NativeByteSequenceStorage s, int cap) {
int oldCapacity = s.getCapacity();
if (cap > oldCapacity) {
int newCapacity = computeNewCapacity(cap);
- Object oldMem = s.getPtr();
- Object newMem = alloc.alloc(newCapacity);
- if (lib.isNull(newMem)) {
- throw raiseNode.raise(inliningTarget, MemoryError);
- }
- // TODO: turn this into a memcpy
- for (long i = 0; i < oldCapacity; i++) {
- write.writeArrayElement(newMem, i, read.readArrayElement(oldMem, i));
- }
- free.free(oldMem);
+ long oldMem = s.getPtr();
+ long newMem = callocByteArray(newCapacity);
+ copyByteArray(newMem, 0, oldMem, 0, oldCapacity);
+ free(oldMem);
s.setPtr(newMem);
s.setCapacity(newCapacity);
}
}
@Specialization
- static void doNativeObject(NativeObjectSequenceStorage s, int cap,
- @Bind Node inliningTarget,
- @Shared @CachedLibrary(limit = "2") InteropLibrary lib,
- @Shared @Cached CStructAccess.AllocateNode alloc,
- @Shared @Cached CStructAccess.FreeNode free,
- @Shared @Cached PRaiseNode raiseNode,
- @Cached CStructAccess.ReadPointerNode read,
- @Cached CStructAccess.WritePointerNode write) {
+ static void doNativeObject(NativeObjectSequenceStorage s, int cap) {
int oldCapacity = s.getCapacity();
if (cap > oldCapacity) {
int newCapacity = computeNewCapacity(cap);
- Object oldMem = s.getPtr();
- long bytes = newCapacity * 8;
- Object newMem = alloc.alloc(bytes);
- if (lib.isNull(newMem)) {
- throw raiseNode.raise(inliningTarget, MemoryError);
- }
- // TODO: turn this into a memcpy
- for (long i = 0; i < oldCapacity; i++) {
- write.writeArrayElement(newMem, i, read.readArrayElement(oldMem, i));
- }
- free.free(oldMem);
+ long oldMem = s.getPtr();
+ long newMem = callocPtrArray(newCapacity);
+ copyPtrArray(newMem, 0, oldMem, 0, oldCapacity);
+ free(oldMem);
s.setPtr(newMem);
s.setCapacity(newCapacity);
}
@@ -3484,15 +3457,13 @@ public abstract static class SetNativeLenNode extends Node {
@InliningCutoff
static void doShrink(NativeObjectSequenceStorage s, int len,
@Bind Node inliningTarget,
- @Cached CStructAccess.ReadPointerNode readNode,
- @Cached CStructAccess.WritePointerNode writeNode,
@Cached CExtNodes.XDecRefPointerNode decRefPointerNode) {
if (len < s.length()) {
// When shrinking, we need to decref the items that are now past the end
for (int i = len; i < s.length(); i++) {
- Object elementPointer = readNode.readArrayElement(s.getPtr(), i);
+ long elementPointer = readPtrArrayElement(s.getPtr(), i);
decRefPointerNode.execute(inliningTarget, elementPointer);
- writeNode.writeArrayElement(s.getPtr(), i, 0L);
+ writePtrArrayElement(s.getPtr(), i, NULLPTR);
}
}
s.setNewLength(len);
@@ -3604,15 +3575,13 @@ static void doGeneric(Node inliningTarget, SequenceStorage s, int idx,
@Specialization
static void doNativeObjectStorage(Node inliningTarget, NativeObjectSequenceStorage s, int idx,
- @Cached(inline = false) CStructAccess.ReadPointerNode readPointerNode,
- @Cached(inline = false) CStructAccess.WritePointerNode writePointerNode,
@Cached CExtNodes.XDecRefPointerNode decRefNode) {
int len = s.length();
- Object deleted = readPointerNode.readArrayElement(s.getPtr(), idx);
+ long deleted = readPtrArrayElement(s.getPtr(), idx);
for (int i = idx; i < len - 1; i++) {
- writePointerNode.writeArrayElement(s.getPtr(), i, readPointerNode.readArrayElement(s.getPtr(), i + 1));
+ writePtrArrayElement(s.getPtr(), i, readPtrArrayElement(s.getPtr(), i + 1));
}
- writePointerNode.writeArrayElement(s.getPtr(), len - 1, 0L);
+ writePtrArrayElement(s.getPtr(), len - 1, NULLPTR);
s.setNewLength(len - 1);
decRefNode.execute(inliningTarget, deleted);
}
@@ -4099,15 +4068,13 @@ static SequenceStorage doNativeStorage(Node inliningTarget, NativeIntSequenceSto
@Specialization
protected static SequenceStorage doNativeObjectStorage(Node inliningTarget, NativeObjectSequenceStorage storage, int index, Object value,
@Exclusive @Cached EnsureCapacityNode ensureCapacityNode,
- @Cached(inline = false) CStructAccess.ReadPointerNode readPointerNode,
- @Cached(inline = false) CStructAccess.WritePointerNode writePointerNode,
@Cached PythonToNativeNewRefNode toNative) {
int newLength = storage.length() + 1;
ensureCapacityNode.execute(inliningTarget, storage, newLength);
for (int i = storage.length(); i > index; i--) {
- writePointerNode.writeArrayElement(storage.getPtr(), i, readPointerNode.readArrayElement(storage.getPtr(), i - 1));
+ writePtrArrayElement(storage.getPtr(), i, readPtrArrayElement(storage.getPtr(), i - 1));
}
- writePointerNode.writeArrayElement(storage.getPtr(), index, toNative.execute(value));
+ writePtrArrayElement(storage.getPtr(), index, toNative.executeLong(value));
storage.setNewLength(newLength);
return storage;
}
@@ -4115,15 +4082,13 @@ protected static SequenceStorage doNativeObjectStorage(Node inliningTarget, Nati
@Specialization
protected static SequenceStorage doNativeByteStorage(Node inliningTarget, NativeByteSequenceStorage storage, int index, Object value,
@Exclusive @Cached EnsureCapacityNode ensureCapacityNode,
- @Cached(inline = false) CStructAccess.ReadByteNode readByteNode,
- @Cached(inline = false) CStructAccess.WriteByteNode writeByteNode,
@Cached CastToByteNode castToByteNode) {
int newLength = storage.length() + 1;
ensureCapacityNode.execute(inliningTarget, storage, newLength);
for (int i = storage.length(); i > index; i--) {
- writeByteNode.writeArrayElement(storage.getPtr(), i, readByteNode.readArrayElement(storage.getPtr(), i - 1));
+ writeByteArrayElement(storage.getPtr(), i, readByteArrayElement(storage.getPtr(), i - 1));
}
- writeByteNode.writeArrayElement(storage.getPtr(), index, castToByteNode.execute(null, value));
+ writeByteArrayElement(storage.getPtr(), index, castToByteNode.execute(null, value));
storage.setNewLength(newLength);
return storage;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java
index 4a62ca6b0b..bf764875f4 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java
@@ -42,6 +42,7 @@
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyComplexObject__cval__imag;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyComplexObject__cval__real;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readDoubleField;
import static com.oracle.graal.python.nodes.BuiltinNames.J_COMPLEX;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___COMPLEX__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___FORMAT__;
@@ -53,6 +54,7 @@
import static com.oracle.graal.python.runtime.exception.PythonErrorType.ZeroDivisionError;
import static com.oracle.graal.python.runtime.formatting.FormattingUtils.validateForFloat;
+import java.lang.ref.Reference;
import java.util.List;
import com.oracle.graal.python.PythonLanguage;
@@ -70,11 +72,13 @@
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.PNotImplemented;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.FormatNodeBase;
import com.oracle.graal.python.builtins.objects.complex.ComplexBuiltinsClinicProviders.FormatNodeClinicProviderGen;
import com.oracle.graal.python.builtins.objects.floats.FloatBuiltins;
@@ -112,6 +116,7 @@
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles;
import com.oracle.graal.python.nodes.truffle.PythonIntegerAndFloatTypes;
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
@@ -183,10 +188,9 @@ static ComplexValue doComplex(PComplex v) {
@Specialization(guards = "check.execute(inliningTarget, v)", limit = "1")
@InliningCutoff
static ComplexValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject v,
- @SuppressWarnings("unused") @Cached PyComplexCheckNode check,
- @Cached(inline = false) CStructAccess.ReadDoubleNode read) {
- double real = read.readFromObj(v, PyComplexObject__cval__real);
- double imag = read.readFromObj(v, PyComplexObject__cval__imag);
+ @SuppressWarnings("unused") @Cached PyComplexCheckNode check) {
+ double real = readDoubleField(v.getPtr(), PyComplexObject__cval__real);
+ double imag = readDoubleField(v.getPtr(), PyComplexObject__cval__imag);
return new ComplexValue(real, imag);
}
@@ -236,6 +240,8 @@ public abstract static class ComplexNewNode extends PythonTernaryBuiltinNode {
@GenerateCached(false)
@GenerateUncached
abstract static class CreateComplexNode extends Node {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_COMPLEX_SUBTYPE_FROM_DOUBLES);
+
public abstract Object execute(Node inliningTarget, Object cls, double real, double imaginary);
public static Object executeUncached(Object cls, double real, double imaginary) {
@@ -251,13 +257,23 @@ static PComplex doManaged(@SuppressWarnings("unused") Node inliningTarget, Objec
@Fallback
static Object doNative(Node inliningTarget, Object cls, double real, double imaginary,
- @Cached(inline = false) CExtNodes.PCallCapiFunction callCapiFunction,
@Cached(inline = false) CApiTransitions.PythonToNativeNode toNativeNode,
@Cached(inline = false) CApiTransitions.NativeToPythonTransferNode toPythonNode,
- @Cached(inline = false) ExternalFunctionNodes.DefaultCheckFunctionResultNode checkFunctionResultNode) {
+ @Cached(inline = false) PyObjectCheckFunctionResultNode checkFunctionResultNode) {
NativeCAPISymbol symbol = NativeCAPISymbol.FUN_COMPLEX_SUBTYPE_FROM_DOUBLES;
- Object nativeResult = callCapiFunction.call(symbol, toNativeNode.execute(cls), real, imaginary);
- return toPythonNode.execute(checkFunctionResultNode.execute(PythonContext.get(inliningTarget), symbol.getTsName(), nativeResult));
+ // classes are always Python objects
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(cls);
+ long clsPointer = toNativeNode.executeLong(cls);
+ try {
+ PythonContext context = PythonContext.get(inliningTarget);
+ var callable = CApiContext.getNativeSymbol(inliningTarget, symbol);
+ long nativeResult = ExternalFunctionInvoker.invokeCOMPLEX_SUBTYPE_FROM_DOUBLES(null, C_API_TIMING,
+ context.ensureNativeContext(), BoundaryCallData.getUncached(), context.getThreadState(PythonLanguage.get(inliningTarget)), callable,
+ clsPointer, real, imaginary);
+ return checkFunctionResultNode.execute(context, symbol.getTsName(), toPythonNode.execute(nativeResult));
+ } finally {
+ Reference.reachabilityFence(cls);
+ }
}
}
@@ -1412,9 +1428,8 @@ static double get(PComplex self) {
@Specialization
@InliningCutoff
- static double getNative(PythonAbstractNativeObject self,
- @Cached CStructAccess.ReadDoubleNode read) {
- return read.readFromObj(self, PyComplexObject__cval__real);
+ static double getNative(PythonAbstractNativeObject self) {
+ return readDoubleField(self.getPtr(), PyComplexObject__cval__real);
}
}
@@ -1428,9 +1443,8 @@ static double get(PComplex self) {
@Specialization
@InliningCutoff
- static double getNative(PythonAbstractNativeObject self,
- @Cached CStructAccess.ReadDoubleNode read) {
- return read.readFromObj(self, PyComplexObject__cval__imag);
+ static double getNative(PythonAbstractNativeObject self) {
+ return readDoubleField(self.getPtr(), PyComplexObject__cval__imag);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionBuiltins.java
index 2a0fc3b46b..f0194a8bf3 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/BaseExceptionBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2025, Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2026, Oracle and/or its affiliates.
* Copyright (c) 2014, Regents of the University of California
*
* All rights reserved.
@@ -42,6 +42,7 @@
import static com.oracle.graal.python.nodes.StringLiterals.T_LPAREN;
import static com.oracle.graal.python.nodes.StringLiterals.T_RPAREN;
+import java.lang.ref.Reference;
import java.util.List;
import com.oracle.graal.python.PythonLanguage;
@@ -54,9 +55,12 @@
import com.oracle.graal.python.builtins.PythonBuiltins;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageForEach;
@@ -97,6 +101,7 @@
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToJavaBooleanNode;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
import com.oracle.graal.python.runtime.object.PFactory;
@@ -135,6 +140,7 @@ protected List extends NodeFactory extends PythonBuiltinBaseNode>> getNodeFa
@SlotSignature(name = "BaseException", minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true)
@GenerateNodeFactory
public abstract static class BaseExceptionNode extends PythonVarargsBuiltinNode {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_EXCEPTION_SUBTYPE_NEW);
@Specialization(guards = "!needsNativeAllocationNode.execute(inliningTarget, cls)", limit = "1")
static Object doManaged(Object cls, @SuppressWarnings("unused") Object[] args, @SuppressWarnings("unused") PKeyword[] kwargs,
@@ -157,13 +163,24 @@ static Object doNativeSubtype(Object cls, Object[] args, @SuppressWarnings("unus
@SuppressWarnings("unused") @Bind Node inliningTarget,
@SuppressWarnings("unused") @Cached.Exclusive @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode,
@Bind PythonLanguage language,
- @Cached CExtNodes.PCallCapiFunction callCapiFunction,
@Cached CApiTransitions.PythonToNativeNode toNativeNode,
@Cached CApiTransitions.NativeToPythonTransferNode toPythonNode,
- @Cached ExternalFunctionNodes.DefaultCheckFunctionResultNode checkFunctionResultNode) {
+ @Cached PyObjectCheckFunctionResultNode checkFunctionResultNode) {
Object argsTuple = args.length > 0 ? PFactory.createTuple(language, args) : PFactory.createEmptyTuple(language);
- Object nativeResult = callCapiFunction.call(NativeCAPISymbol.FUN_EXCEPTION_SUBTYPE_NEW, toNativeNode.execute(cls), toNativeNode.execute(argsTuple));
- return toPythonNode.execute(checkFunctionResultNode.execute(PythonContext.get(inliningTarget), NativeCAPISymbol.FUN_EXCEPTION_SUBTYPE_NEW.getTsName(), nativeResult));
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(cls);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(argsTuple);
+ long clsPointer = toNativeNode.executeLong(cls);
+ long argsTuplePointer = toNativeNode.executeLong(argsTuple);
+ try {
+ PythonContext context = PythonContext.get(inliningTarget);
+ var callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_EXCEPTION_SUBTYPE_NEW);
+ long nativeResult = ExternalFunctionInvoker.invokeEXCEPTION_SUBTYPE_NEW(null, C_API_TIMING, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(), context.getThreadState(PythonLanguage.get(inliningTarget)), callable, clsPointer, argsTuplePointer);
+ return checkFunctionResultNode.execute(context, NativeCAPISymbol.FUN_EXCEPTION_SUBTYPE_NEW.getTsName(), toPythonNode.execute(nativeResult));
+ } finally {
+ Reference.reachabilityFence(cls);
+ Reference.reachabilityFence(argsTuple);
+ }
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/ExceptionNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/ExceptionNodes.java
index f987db30d5..20b9a8b5a2 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/ExceptionNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/ExceptionNodes.java
@@ -41,6 +41,8 @@
package com.oracle.graal.python.builtins.objects.exception;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readByteField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeByteField;
import static com.oracle.graal.python.nodes.StringLiterals.T_COLON_SPACE;
import static com.oracle.graal.python.nodes.StringLiterals.T_NO_MESSAGE;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
@@ -61,7 +63,6 @@
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
-import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.formatting.ErrorMessageFormatter;
import com.oracle.graal.python.runtime.object.PFactory;
@@ -94,8 +95,8 @@ private static Object noValueToNone(Object obj) {
return obj != PNone.NO_VALUE ? obj : PNone.NONE;
}
- private static Object noneToNativeNull(Node node, Object obj) {
- return obj != PNone.NONE ? obj : PythonContext.get(node).getNativeNull();
+ private static Object noneToNativeNull(@SuppressWarnings("unused") Node node, Object obj) {
+ return obj != PNone.NONE ? obj : PNone.NO_VALUE;
}
@GenerateUncached
@@ -151,10 +152,9 @@ static void doManaged(PBaseException exception, Object value) {
@Specialization(guards = "check.execute(inliningTarget, exception)", limit = "1")
static void doNative(Node inliningTarget, PythonAbstractNativeObject exception, Object value,
@SuppressWarnings("unused") @Cached PyExceptionInstanceCheckNode check,
- @Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObject,
- @Cached(inline = false) CStructAccess.WriteByteNode writeByte) {
+ @Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObject) {
writeObject.writeToObject(exception, CFields.PyBaseExceptionObject__cause, noneToNativeNull(inliningTarget, value));
- writeByte.writeToObject(exception, CFields.PyBaseExceptionObject__suppress_context, (byte) 1);
+ writeByteField(exception.getPtr(), CFields.PyBaseExceptionObject__suppress_context, (byte) 1);
}
@Specialization
@@ -244,11 +244,9 @@ static boolean doManaged(PBaseException exception) {
}
@Specialization(guards = "check.execute(inliningTarget, exception)", limit = "1")
- static boolean doNative(Node inliningTarget, PythonAbstractNativeObject exception,
- @SuppressWarnings("unused") @Cached PyExceptionInstanceCheckNode check,
- @Cached(inline = false) CStructAccess.ReadByteNode read) {
-
- return read.readFromObj(exception, CFields.PyBaseExceptionObject__suppress_context) != 0;
+ static boolean doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject exception,
+ @SuppressWarnings("unused") @Cached PyExceptionInstanceCheckNode check) {
+ return readByteField(exception.getPtr(), CFields.PyBaseExceptionObject__suppress_context) != 0;
}
@Specialization
@@ -275,9 +273,8 @@ static void doManaged(PBaseException exception, boolean value) {
@Specialization(guards = "check.execute(inliningTarget, exception)", limit = "1")
static void doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject exception, boolean value,
- @SuppressWarnings("unused") @Cached PyExceptionInstanceCheckNode check,
- @Cached(inline = false) CStructAccess.WriteByteNode write) {
- write.writeToObject(exception, CFields.PyBaseExceptionObject__suppress_context, value ? (byte) 1 : (byte) 0);
+ @SuppressWarnings("unused") @Cached PyExceptionInstanceCheckNode check) {
+ writeByteField(exception.getPtr(), CFields.PyBaseExceptionObject__suppress_context, value ? (byte) 1 : (byte) 0);
}
@Specialization
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java
index 8aa519dbde..48e340f7ad 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/FloatBuiltins.java
@@ -296,7 +296,7 @@ static Object floatFromNoneManagedSubclass(Object cls, PNone obj, @SuppressWarni
static Object floatFromObjectManagedSubclass(VirtualFrame frame, Object cls, Object obj, @SuppressWarnings("unused") boolean needsNativeAllocation,
@Bind Node inliningTarget,
@Shared @Cached TypeNodes.GetInstanceShape getInstanceShape,
- @Shared @Cached PrimitiveFloatNode recursiveCallNode) {
+ @Exclusive @Cached PrimitiveFloatNode recursiveCallNode) {
Shape shape = getInstanceShape.execute(cls);
return PFactory.createFloat(cls, shape, recursiveCallNode.execute(frame, inliningTarget, obj));
}
@@ -309,10 +309,10 @@ static Object floatFromObjectManagedSubclass(VirtualFrame frame, Object cls, Obj
@InliningCutoff
static Object floatFromObjectNativeSubclass(VirtualFrame frame, Object cls, Object obj, @SuppressWarnings("unused") boolean needsNativeAllocation,
@Bind Node inliningTarget,
- @Cached @SuppressWarnings("unused") IsSubtypeNode isSubtype,
- @Cached CExtNodes.FloatSubtypeNew subtypeNew,
- @Shared @Cached PrimitiveFloatNode recursiveCallNode) {
- return subtypeNew.call(cls, recursiveCallNode.execute(frame, inliningTarget, obj));
+ @Exclusive @Cached @SuppressWarnings("unused") IsSubtypeNode isSubtype,
+ @Exclusive @Cached CExtNodes.FloatSubtypeNew subtypeNew,
+ @Exclusive @Cached PrimitiveFloatNode recursiveCallNode) {
+ return subtypeNew.execute(inliningTarget, cls, recursiveCallNode.execute(frame, inliningTarget, obj));
}
protected static boolean isSubtypeOfFloat(IsSubtypeNode isSubtypeNode, Object cls) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/PFloat.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/PFloat.java
index bd1f56bb13..6bda369868 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/PFloat.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/floats/PFloat.java
@@ -70,10 +70,6 @@ public String toString() {
return Double.toString(value);
}
- public boolean isNative() {
- return getNativeWrapper() != null && getNativeWrapper().isNative();
- }
-
public static PFloat create(PythonLanguage lang, double value) {
return create(PythonBuiltinClassType.PFloat, PythonBuiltinClassType.PFloat.getInstanceShape(lang), value);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java
index 530ca4895a..c245c9e399 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/frame/PFrame.java
@@ -222,7 +222,7 @@ public PFrame(PythonLanguage lang, Reference virtualFrameInfo, Node location, Ob
this.thread = Thread.currentThread();
}
- public PFrame(PythonLanguage lang, @SuppressWarnings("unused") Object threadState, PCode code, PythonObject globals, Object localsDict) {
+ public PFrame(PythonLanguage lang, @SuppressWarnings("unused") long threadState, PCode code, PythonObject globals, Object localsDict) {
super(PythonBuiltinClassType.PFrame, PythonBuiltinClassType.PFrame.getInstanceShape(lang));
// TODO: frames: extract the information from the threadState object
this.globals = globals;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java
index 5a20717771..63c8209d8c 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java
@@ -81,9 +81,7 @@
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes.BytesFromObject;
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromNativeSubclassNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PointerCompareNode;
import com.oracle.graal.python.builtins.objects.common.FormatNodeBase;
import com.oracle.graal.python.builtins.objects.floats.PFloat;
import com.oracle.graal.python.builtins.objects.ints.IntBuiltinsClinicProviders.FormatNodeClinicProviderGen;
@@ -120,7 +118,6 @@
import com.oracle.graal.python.lib.RichCmpOp;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PGuards;
-import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.SpecialMethodNames;
import com.oracle.graal.python.nodes.call.CallNode;
@@ -167,8 +164,6 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.dsl.TypeSystemReference;
import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
@@ -2064,53 +2059,6 @@ long doInteger(long left, long right) {
return op(left, right);
}
- @Specialization(guards = "a.isNativePointer()")
- Object opVoidNativePtrLong(PythonNativeVoidPtr a, long b) {
- if (a.isNativePointer()) {
- return op(a.getNativePointer(), b);
- }
- return PNotImplemented.NOT_IMPLEMENTED;
- }
-
- @Specialization(guards = "!a.isNativePointer()")
- Object opVoidPtrLong(VirtualFrame frame, PythonNativeVoidPtr a, long b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- return op(hashNode.execute(frame, inliningTarget, a), b);
- }
-
- @Specialization(guards = {"a.isNativePointer()", "b.isNativePointer()"})
- long voidPtrsNative(PythonNativeVoidPtr a, PythonNativeVoidPtr b) {
- long ptrVal = a.getNativePointer();
- // pointers are considered unsigned
- return op(ptrVal, b.getNativePointer());
- }
-
- @Specialization(guards = {"a.isNativePointer()", "!b.isNativePointer()"})
- long voidPtrsANative(VirtualFrame frame, PythonNativeVoidPtr a, PythonNativeVoidPtr b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- long ptrVal = a.getNativePointer();
- // pointers are considered unsigned
- return op(ptrVal, hashNode.execute(frame, inliningTarget, b));
- }
-
- @Specialization(guards = {"!a.isNativePointer()", "b.isNativePointer()"})
- long voidPtrsBNative(VirtualFrame frame, PythonNativeVoidPtr a, PythonNativeVoidPtr b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- long ptrVal = b.getNativePointer();
- // pointers are considered unsigned
- return op(ptrVal, hashNode.execute(frame, inliningTarget, a));
- }
-
- @Specialization(guards = {"!a.isNativePointer()", "!b.isNativePointer()"})
- long voidPtrsManaged(VirtualFrame frame, PythonNativeVoidPtr a, PythonNativeVoidPtr b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- return op(hashNode.execute(frame, inliningTarget, a), hashNode.execute(frame, inliningTarget, b));
- }
-
@Specialization
PInt doPInt(long left, PInt right,
@Bind PythonLanguage language) {
@@ -2292,113 +2240,6 @@ static boolean doDN(VirtualFrame frame, Node inliningTarget, PythonAbstractNativ
// Note: native int subclasses are still represented as Java PInt, just with a different
// Python level class
- static boolean someIsNativePtr(Object a, Object b) {
- return a instanceof PythonNativeVoidPtr || b instanceof PythonNativeVoidPtr;
- }
-
- @Specialization(guards = "someIsNativePtr(x, y)")
- @InliningCutoff
- static Object doVoidPtr(VirtualFrame frame, Node inliningTarget, Object x, Object y, RichCmpOp op,
- @Cached PointerCompareNode pointerCompareNode,
- @Cached EqNodeNativePtr pointerEqNode) {
- if (op.isEqOrNe()) {
- Object result = pointerEqNode.execute(frame, x, y);
- if (result == PNotImplemented.NOT_IMPLEMENTED) {
- return result;
- }
- return ((boolean) result) == op.isEq();
- }
- return pointerCompareNode.execute(inliningTarget, op, x, y);
- }
-
- @GenerateInline(false) // footprint reduction 32 -> 15
- @TypeSystemReference(PythonIntegerTypes.class)
- abstract static class EqNodeNativePtr extends PNodeWithContext {
-
- abstract Object execute(VirtualFrame frame, Object a, Object b);
-
- @Specialization
- static boolean eqLongVoidPtr(VirtualFrame frame, long a, PythonNativeVoidPtr b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- return eqVoidPtrLong(frame, b, a, inliningTarget, hashNode);
- }
-
- @Specialization
- static boolean eqPIntVoidPtr(PInt a, PythonNativeVoidPtr b) {
- return eqVoidPtrPInt(b, a);
- }
-
- @Specialization
- static boolean eqVoidPtrLong(VirtualFrame frame, PythonNativeVoidPtr a, long b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- if (a.isNativePointer()) {
- long ptrVal = a.getNativePointer();
- // pointers are considered unsigned
- return ptrVal == b;
- }
- return hashNode.execute(frame, inliningTarget, a) == b;
- }
-
- @Specialization(guards = {"a.isNativePointer()", "b.isNativePointer()"})
- static boolean voidPtrsNative(PythonNativeVoidPtr a, PythonNativeVoidPtr b) {
- long ptrVal = a.getNativePointer();
- // pointers are considered unsigned
- return ptrVal == b.getNativePointer();
- }
-
- @Specialization(guards = {"a.isNativePointer()", "!b.isNativePointer()"})
- static boolean voidPtrsANative(VirtualFrame frame, PythonNativeVoidPtr a, PythonNativeVoidPtr b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- long ptrVal = a.getNativePointer();
- // pointers are considered unsigned
- return ptrVal == hashNode.execute(frame, inliningTarget, b);
- }
-
- @Specialization(guards = {"!a.isNativePointer()", "b.isNativePointer()"})
- static boolean voidPtrsBNative(VirtualFrame frame, PythonNativeVoidPtr a, PythonNativeVoidPtr b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- long ptrVal = b.getNativePointer();
- // pointers are considered unsigned
- return ptrVal == hashNode.execute(frame, inliningTarget, a);
- }
-
- @Specialization(guards = {"!a.isNativePointer()", "!b.isNativePointer()"})
- static boolean voidPtrsManaged(VirtualFrame frame, PythonNativeVoidPtr a, PythonNativeVoidPtr b,
- @Bind Node inliningTarget,
- @Shared("h") @Cached PyObjectHashNode hashNode) {
- return hashNode.execute(frame, inliningTarget, a) == hashNode.execute(frame, inliningTarget, b);
- }
-
- @Specialization
- @TruffleBoundary
- static boolean eqVoidPtrPInt(PythonNativeVoidPtr a, PInt b) {
- if (a.isNativePointer()) {
- long ptrVal = a.getNativePointer();
- if (ptrVal < 0) {
- // pointers are considered unsigned
- BigInteger bi = PInt.longToBigInteger(ptrVal).add(BigInteger.ONE.shiftLeft(64));
- return bi.equals(b.getValue());
- }
- return PInt.longToBigInteger(ptrVal).equals(b.getValue());
- }
- try {
- return PyObjectHashNode.executeUncached(a) == b.longValueExact();
- } catch (OverflowException e) {
- return false;
- }
- }
-
- @SuppressWarnings("unused")
- @Fallback
- static PNotImplemented doGeneric(Object a, Object b) {
- return PNotImplemented.NOT_IMPLEMENTED;
- }
- }
-
@SuppressWarnings("unused")
@Fallback
static PNotImplemented doGeneric(Object a, Object b, RichCmpOp op) {
@@ -2680,12 +2521,6 @@ static boolean toBoolean(long self) {
static boolean toBoolean(PInt self) {
return !self.isZero();
}
-
- @Specialization
- static boolean toBoolean(PythonNativeVoidPtr self,
- @CachedLibrary(limit = "1") InteropLibrary lib) {
- return !lib.isNull(self.getPointerObject());
- }
}
@Slot(value = SlotKind.tp_str, isComplex = true)
@@ -2695,7 +2530,7 @@ abstract static class StrNode extends PythonUnaryBuiltinNode {
@Specialization
static TruffleString doL(long self,
- @Shared("fromLong") @Cached FromLongNode fromLongNode) {
+ @Cached FromLongNode fromLongNode) {
return fromLongNode.execute(self, TS_ENCODING, false);
}
@@ -2744,14 +2579,6 @@ static TruffleString doPInt(PInt self,
private static int positiveBitLength(PInt self) {
return self.abs().bitLength();
}
-
- @Specialization
- static TruffleString doNativeVoidPtr(VirtualFrame frame, PythonNativeVoidPtr self,
- @Bind Node inliningTarget,
- @Cached PyObjectHashNode hashNode,
- @Shared("fromLong") @Cached FromLongNode fromLongNode) {
- return doL(hashNode.execute(frame, inliningTarget, self), fromLongNode);
- }
}
@Slot(value = SlotKind.tp_repr, isComplex = true)
@@ -2883,20 +2710,6 @@ static long hash(PInt self) {
return self.hash();
}
- @Specialization(limit = "1")
- static long hash(PythonNativeVoidPtr self,
- @CachedLibrary("self.getPointerObject()") InteropLibrary lib) {
- Object object = self.getPointerObject();
- if (lib.hasIdentity(object)) {
- try {
- return lib.identityHashCode(object);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
- }
- return hashCodeBoundary(object);
- }
-
@TruffleBoundary
private static long hashCodeBoundary(Object object) {
return object.hashCode();
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/PInt.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/PInt.java
index 66db371233..ca134fc083 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/PInt.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/PInt.java
@@ -124,10 +124,7 @@ public boolean isNumber(
@Shared("isBoolean") @Cached InlinedConditionProfile isBoolean,
@CachedLibrary("this") InteropLibrary self) {
PythonContext context = PythonContext.get(self);
- if (isBoolean.profile(inliningTarget, this == context.getTrue() || this == context.getFalse())) {
- return false;
- }
- return true;
+ return !isBoolean.profile(inliningTarget, this == context.getTrue() || this == context.getFalse());
}
@ExportMessage
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java
index 469e957785..ea20fa3462 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/iterator/IteratorNodes.java
@@ -57,6 +57,7 @@
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.iterator.IteratorNodesFactory.GetInternalIteratorSequenceStorageNodeGen;
+import com.oracle.graal.python.builtins.objects.iterator.IteratorNodesFactory.GetLengthNodeGen;
import com.oracle.graal.python.builtins.objects.list.PList;
import com.oracle.graal.python.builtins.objects.set.PSet;
import com.oracle.graal.python.builtins.objects.str.PString;
@@ -118,6 +119,11 @@ public abstract static class GetLength extends PNodeWithContext {
public abstract int execute(VirtualFrame frame, Node inliningTarget, Object iterable);
+ @TruffleBoundary
+ public static int executeUncached(Object iterable) {
+ return GetLengthNodeGen.getUncached().execute(null, null, iterable);
+ }
+
// Note: these fast-paths are duplicated in PyObjectSizeNode, because there is no simple
// way to share them effectively without unnecessary indirections and overhead in the
// interpreter
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/CExtPyBuffer.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/CExtPyBuffer.java
index 1a2f50efc8..23829ac250 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/CExtPyBuffer.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/CExtPyBuffer.java
@@ -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
@@ -71,7 +71,7 @@
@ValueType
public final class CExtPyBuffer implements TruffleObject {
/** An object behaving like a {@code void*} pointer. */
- private final Object buf;
+ private final long buf;
private final Object obj;
private final int len;
private final int itemSize;
@@ -83,7 +83,7 @@ public final class CExtPyBuffer implements TruffleObject {
private final int[] suboffsets;
private final Object internal;
- public CExtPyBuffer(Object buf, Object obj, int len, int itemSize, boolean readOnly, int dims, TruffleString format, int[] shape, int[] strides, int[] suboffsets, Object internal) {
+ public CExtPyBuffer(long buf, Object obj, int len, int itemSize, boolean readOnly, int dims, TruffleString format, int[] shape, int[] strides, int[] suboffsets, Object internal) {
this.buf = buf;
this.obj = obj;
this.len = len;
@@ -97,7 +97,7 @@ public CExtPyBuffer(Object buf, Object obj, int len, int itemSize, boolean readO
this.internal = internal;
}
- public Object getBuf() {
+ public long getBuf() {
return buf;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewBuiltins.java
index b4d9d4b8b8..405717c9a5 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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
@@ -73,8 +73,6 @@
import com.oracle.graal.python.builtins.objects.bytes.BytesCommonBuiltins.SepExpectByteNode;
import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
-import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis;
@@ -368,8 +366,7 @@ public abstract static class MemoryViewEqNode extends PNodeWithContext {
private static boolean eq(VirtualFrame frame, Node inliningTarget, PMemoryView self, PMemoryView other,
PyObjectRichCompareBool eqNode,
MemoryViewNodes.ReadItemAtNode readSelf,
- MemoryViewNodes.ReadItemAtNode readOther,
- CExtNodes.PCallCapiFunction callCapiFunction) {
+ MemoryViewNodes.ReadItemAtNode readOther) {
if (self.isReleased() || other.isReleased()) {
return self == other;
}
@@ -397,7 +394,7 @@ private static boolean eq(VirtualFrame frame, Node inliningTarget, PMemoryView s
return eqNode.executeEq(frame, inliningTarget, selfItem, otherItem);
}
- return recursive(frame, inliningTarget, eqNode, callCapiFunction, self, other, readSelf, readOther, 0, ndim,
+ return recursive(frame, inliningTarget, eqNode, self, other, readSelf, readOther, 0, ndim,
self.getBufferPointer(), self.getOffset(), other.getBufferPointer(), other.getOffset());
}
@@ -408,8 +405,7 @@ static Object eq(VirtualFrame frame, Node inliningTarget, PMemoryView self, Obje
@Cached MemoryViewNodes.ReleaseNode releaseNode,
@Cached PyObjectRichCompareBool eqNode,
@Cached MemoryViewNodes.ReadItemAtNode readSelf,
- @Cached MemoryViewNodes.ReadItemAtNode readOther,
- @Cached CExtNodes.PCallCapiFunction callCapiFunction) {
+ @Cached MemoryViewNodes.ReadItemAtNode readOther) {
PMemoryView memoryView;
boolean otherIsMemoryView = otherIsMemoryViewProfile.profile(inliningTarget, other instanceof PMemoryView);
if (otherIsMemoryView) {
@@ -422,7 +418,7 @@ static Object eq(VirtualFrame frame, Node inliningTarget, PMemoryView self, Obje
}
}
try {
- return eq(frame, inliningTarget, self, memoryView, eqNode, readSelf, readOther, callCapiFunction);
+ return eq(frame, inliningTarget, self, memoryView, eqNode, readSelf, readOther);
} finally {
if (!otherIsMemoryView) {
releaseNode.execute(frame, memoryView);
@@ -438,23 +434,23 @@ static Object eq(Object self, Object other) {
// TODO: recursion in PE
@InliningCutoff
- private static boolean recursive(VirtualFrame frame, Node inliningTarget, PyObjectRichCompareBool eqNode, CExtNodes.PCallCapiFunction callCapiFunction,
+ private static boolean recursive(VirtualFrame frame, Node inliningTarget, PyObjectRichCompareBool eqNode,
PMemoryView self, PMemoryView other,
ReadItemAtNode readSelf, ReadItemAtNode readOther,
- int dim, int ndim, Object selfPtr, int initialSelfOffset, Object otherPtr, int initialOtherOffset) {
+ int dim, int ndim, long selfPtr, int initialSelfOffset, long otherPtr, int initialOtherOffset) {
int selfOffset = initialSelfOffset;
int otherOffset = initialOtherOffset;
for (int i = 0; i < self.getBufferShape()[dim]; i++) {
- Object selfXPtr = selfPtr;
+ long selfXPtr = selfPtr;
int selfXOffset = selfOffset;
- Object otherXPtr = otherPtr;
+ long otherXPtr = otherPtr;
int otherXOffset = otherOffset;
if (self.getBufferSuboffsets() != null && self.getBufferSuboffsets()[dim] >= 0) {
- selfXPtr = callCapiFunction.call(NativeCAPISymbol.FUN_ADD_SUBOFFSET, selfPtr, selfOffset, self.getBufferSuboffsets()[dim]);
+ selfXPtr = MemoryViewNodes.addSuboffset(selfPtr, selfOffset, self.getBufferSuboffsets()[dim]);
selfXOffset = 0;
}
if (other.getBufferSuboffsets() != null && other.getBufferSuboffsets()[dim] >= 0) {
- otherXPtr = callCapiFunction.call(NativeCAPISymbol.FUN_ADD_SUBOFFSET, otherPtr, otherOffset, other.getBufferSuboffsets()[dim]);
+ otherXPtr = MemoryViewNodes.addSuboffset(otherPtr, otherOffset, other.getBufferSuboffsets()[dim]);
otherXOffset = 0;
}
if (dim == ndim - 1) {
@@ -464,7 +460,7 @@ private static boolean recursive(VirtualFrame frame, Node inliningTarget, PyObje
return false;
}
} else {
- if (!recursive(frame, inliningTarget, eqNode, callCapiFunction, self, other, readSelf, readOther, dim + 1, ndim, selfXPtr, selfXOffset, otherXPtr, otherXOffset)) {
+ if (!recursive(frame, inliningTarget, eqNode, self, other, readSelf, readOther, dim + 1, ndim, selfXPtr, selfXOffset, otherXPtr, otherXOffset)) {
return false;
}
}
@@ -478,8 +474,6 @@ private static boolean recursive(VirtualFrame frame, Node inliningTarget, PyObje
@Builtin(name = "tolist", minNumOfPositionalArgs = 1)
@GenerateNodeFactory
public abstract static class ToListNode extends PythonUnaryBuiltinNode {
- @Child private CExtNodes.PCallCapiFunction callCapiFunction;
-
@Specialization(guards = {"self.getDimensions() == cachedDimensions", "cachedDimensions < 8"}, limit = "3")
Object tolistCached(VirtualFrame frame, PMemoryView self,
@Bind Node inliningTarget,
@@ -510,18 +504,18 @@ Object tolist(VirtualFrame frame, PMemoryView self,
}
}
- private PList recursiveBoundary(VirtualFrame frame, PMemoryView self, MemoryViewNodes.ReadItemAtNode readItemAtNode, int dim, int ndim, Object ptr, int offset, PythonLanguage language) {
+ private PList recursiveBoundary(VirtualFrame frame, PMemoryView self, MemoryViewNodes.ReadItemAtNode readItemAtNode, int dim, int ndim, long ptr, int offset, PythonLanguage language) {
return recursive(frame, self, readItemAtNode, dim, ndim, ptr, offset, language);
}
- private PList recursive(VirtualFrame frame, PMemoryView self, MemoryViewNodes.ReadItemAtNode readItemAtNode, int dim, int ndim, Object ptr, int initialOffset, PythonLanguage language) {
+ private PList recursive(VirtualFrame frame, PMemoryView self, MemoryViewNodes.ReadItemAtNode readItemAtNode, int dim, int ndim, long ptr, int initialOffset, PythonLanguage language) {
int offset = initialOffset;
Object[] objects = new Object[self.getBufferShape()[dim]];
for (int i = 0; i < self.getBufferShape()[dim]; i++) {
- Object xptr = ptr;
+ long xptr = ptr;
int xoffset = offset;
if (self.getBufferSuboffsets() != null && self.getBufferSuboffsets()[dim] >= 0) {
- xptr = getCallCapiFunction().call(NativeCAPISymbol.FUN_ADD_SUBOFFSET, ptr, offset, self.getBufferSuboffsets()[dim]);
+ xptr = MemoryViewNodes.addSuboffset(ptr, offset, self.getBufferSuboffsets()[dim]);
xoffset = 0;
}
if (dim == ndim - 1) {
@@ -534,13 +528,6 @@ private PList recursive(VirtualFrame frame, PMemoryView self, MemoryViewNodes.Re
return PFactory.createList(language, objects);
}
- private CExtNodes.PCallCapiFunction getCallCapiFunction() {
- if (callCapiFunction == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- callCapiFunction = insert(CExtNodes.PCallCapiFunction.create());
- }
- return callCapiFunction;
- }
}
@Builtin(name = "tobytes", minNumOfPositionalArgs = 1, parameterNames = {"$self", "order"})
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewIteratorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewIteratorBuiltins.java
index c2985e22c8..c61e8d67f2 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewIteratorBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewIteratorBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -48,8 +48,6 @@
import com.oracle.graal.python.annotations.Slot.SlotKind;
import com.oracle.graal.python.builtins.CoreFunctions;
import com.oracle.graal.python.builtins.PythonBuiltins;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
-import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.TpIterNextBuiltin;
import com.oracle.graal.python.nodes.PRaiseNode;
@@ -86,16 +84,15 @@ static Object exhausted(VirtualFrame frame, MemoryViewIterator self) {
@Specialization(guards = "!self.isExhausted()")
static Object memoryiterNext(VirtualFrame frame, MemoryViewIterator self,
@Bind Node inliningTarget,
- @Cached CExtNodes.PCallCapiFunction capiFunction,
@Cached MemoryViewNodes.ReadItemAtNode readItemAtNode,
@Cached PRaiseNode raiseNode) {
PMemoryView seq = self.getSeq();
if (self.getIndex() < self.getLength()) {
seq.checkReleased(inliningTarget, raiseNode);
- Object ptr = seq.getBufferPointer();
+ long ptr = seq.getBufferPointer();
int offset = seq.getOffset() + seq.getBufferStrides()[0] * self.index++;
if (seq.getBufferSuboffsets() != null && seq.getBufferSuboffsets()[0] >= 0) {
- ptr = capiFunction.call(NativeCAPISymbol.FUN_ADD_SUBOFFSET, ptr, offset, seq.getBufferSuboffsets()[0]);
+ ptr = MemoryViewNodes.addSuboffset(ptr, offset, seq.getBufferSuboffsets()[0]);
offset = 0;
}
return readItemAtNode.execute(frame, seq, ptr, offset);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewNodes.java
index 3709c2a16e..1bf17b91fb 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/MemoryViewNodes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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
@@ -47,10 +47,9 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.BufferStorageNodes;
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
@@ -58,6 +57,7 @@
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
import com.oracle.graal.python.lib.PyIndexCheckNode;
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
@@ -66,6 +66,7 @@
import com.oracle.graal.python.nodes.util.CastToByteNode;
import com.oracle.graal.python.runtime.ExecutionContext.InteropCallContext;
import com.oracle.graal.python.runtime.IndirectCallData.InteropCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.sequence.storage.NativeByteSequenceStorage;
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
@@ -93,6 +94,12 @@
import com.oracle.truffle.api.strings.TruffleString;
public class MemoryViewNodes {
+ /** {@code *(int8_t**)(ptr + offset) + suboffset} */
+ static long addSuboffset(long ptr, int offset, int suboffset) {
+ assert PythonContext.get(null).isNativeAccessAllowed();
+ return NativeMemory.readPtr(ptr + offset) + suboffset;
+ }
+
static boolean isByteFormat(BufferFormat format) {
return format == BufferFormat.UINT_8 || format == BufferFormat.INT_8 || format == BufferFormat.CHAR;
}
@@ -115,6 +122,11 @@ static void checkBufferBounds(Node node, PMemoryView self, PythonBufferAccessLib
public abstract static class InitFlagsNode extends Node {
public abstract int execute(Node inliningTarget, int ndim, int itemsize, int[] shape, int[] strides, int[] suboffsets);
+ @TruffleBoundary
+ public static int executeUncached(int ndim, int itemsize, int[] shape, int[] strides, int[] suboffsets) {
+ return MemoryViewNodesFactory.InitFlagsNodeGen.getUncached().execute(null, ndim, itemsize, shape, strides, suboffsets);
+ }
+
@Specialization
static int compute(int ndim, int itemsize, int[] shape, int[] strides, int[] suboffsets) {
if (ndim == 0) {
@@ -194,18 +206,18 @@ static void notImplemented(Node inliningTarget, BufferFormat format, TruffleStri
@GenerateUncached
@GenerateInline
@GenerateCached(false)
+ @ImportStatic(NativeMemory.class)
abstract static class ReadBytesAtNode extends Node {
- public abstract void execute(Node inliningTarget, byte[] dest, int destOffset, int len, PMemoryView self, Object ptr, int offset);
+ public abstract void execute(Node inliningTarget, byte[] dest, int destOffset, int len, PMemoryView self, long ptr, int offset);
- @Specialization(guards = "ptr != null")
- static void doNativeCached(Node inliningTarget, byte[] dest, int destOffset, @SuppressWarnings("unused") int len, @SuppressWarnings("unused") PMemoryView self, Object ptr, int offset,
- @Exclusive @Cached InlinedIntValueProfile lengthProfile,
- @Cached(inline = false) CStructAccess.ReadByteNode readNode) {
- readNode.readByteArray(ptr, dest, lengthProfile.profile(inliningTarget, len), offset, destOffset);
+ @Specialization(guards = "ptr != NULLPTR")
+ static void doNativeCached(Node inliningTarget, byte[] dest, int destOffset, @SuppressWarnings("unused") int len, @SuppressWarnings("unused") PMemoryView self, long ptr, int offset,
+ @Exclusive @Cached InlinedIntValueProfile lengthProfile) {
+ NativeMemory.readByteArrayElements(ptr, offset, dest, destOffset, lengthProfile.profile(inliningTarget, len));
}
- @Specialization(guards = "ptr == null", limit = "3")
- static void doManagedCached(Node inliningTarget, byte[] dest, int destOffset, @SuppressWarnings("unused") int len, PMemoryView self, @SuppressWarnings("unused") Object ptr, int offset,
+ @Specialization(guards = "ptr == NULLPTR", limit = "3")
+ static void doManagedCached(Node inliningTarget, byte[] dest, int destOffset, @SuppressWarnings("unused") int len, PMemoryView self, @SuppressWarnings("unused") long ptr, int offset,
@CachedLibrary("self.getBuffer()") PythonBufferAccessLibrary bufferLib,
@Exclusive @Cached InlinedIntValueProfile lenProfile) {
int cachedLen = lenProfile.profile(inliningTarget, len);
@@ -217,18 +229,18 @@ static void doManagedCached(Node inliningTarget, byte[] dest, int destOffset, @S
@GenerateUncached
@GenerateInline
@GenerateCached(false)
+ @ImportStatic(NativeMemory.class)
abstract static class WriteBytesAtNode extends Node {
- public abstract void execute(Node inliningTarget, byte[] src, int srcOffset, int len, PMemoryView self, Object ptr, int offset);
+ public abstract void execute(Node inliningTarget, byte[] src, int srcOffset, int len, PMemoryView self, long ptr, int offset);
- @Specialization(guards = "ptr != null")
- static void doNativeCached(Node inliningTarget, byte[] src, int srcOffset, int len, @SuppressWarnings("unused") PMemoryView self, Object ptr, int offset,
- @Exclusive @Cached InlinedIntValueProfile lenProfile,
- @Cached(inline = false) CStructAccess.WriteByteNode writeNode) {
- writeNode.writeByteArray(ptr, src, lenProfile.profile(inliningTarget, len), srcOffset, offset);
+ @Specialization(guards = "ptr != NULLPTR")
+ static void doNativeCached(Node inliningTarget, byte[] src, int srcOffset, int len, @SuppressWarnings("unused") PMemoryView self, long ptr, int offset,
+ @Exclusive @Cached InlinedIntValueProfile lenProfile) {
+ NativeMemory.writeByteArrayElements(ptr, offset, src, srcOffset, lenProfile.profile(inliningTarget, len));
}
- @Specialization(guards = "ptr == null", limit = "3")
- static void doManagedCached(Node inliningTarget, byte[] src, int srcOffset, int len, PMemoryView self, @SuppressWarnings("unused") Object ptr, int offset,
+ @Specialization(guards = "ptr == NULLPTR", limit = "3")
+ static void doManagedCached(Node inliningTarget, byte[] src, int srcOffset, int len, PMemoryView self, @SuppressWarnings("unused") long ptr, int offset,
@CachedLibrary("self.getBuffer()") PythonBufferAccessLibrary bufferLib,
@Exclusive @Cached InlinedIntValueProfile lenProfile) {
int cachedLen = lenProfile.profile(inliningTarget, len);
@@ -238,11 +250,12 @@ static void doManagedCached(Node inliningTarget, byte[] src, int srcOffset, int
}
@GenerateInline(false) // footprint reduction 48 -> 29
+ @ImportStatic(NativeMemory.class)
abstract static class ReadItemAtNode extends Node {
- public abstract Object execute(VirtualFrame frame, PMemoryView self, Object ptr, int offset);
+ public abstract Object execute(VirtualFrame frame, PMemoryView self, long ptr, int offset);
- @Specialization(guards = "ptr != null")
- static Object doNative(PMemoryView self, Object ptr, int offset,
+ @Specialization(guards = "ptr != NULLPTR")
+ static Object doNative(PMemoryView self, long ptr, int offset,
@Bind Node inliningTarget,
@Shared @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib,
@Shared @Cached UnpackValueNode unpackValueNode) {
@@ -252,8 +265,8 @@ static Object doNative(PMemoryView self, Object ptr, int offset,
return unpackValueNode.execute(inliningTarget, self.getFormat(), self.getFormatString(), buffer, offset);
}
- @Specialization(guards = "ptr == null")
- static Object doManaged(PMemoryView self, @SuppressWarnings("unused") Object ptr, int offset,
+ @Specialization(guards = "ptr == NULLPTR")
+ static Object doManaged(PMemoryView self, @SuppressWarnings("unused") long ptr, int offset,
@Bind Node inliningTarget,
@Shared @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib,
@Shared @Cached UnpackValueNode unpackValueNode) {
@@ -269,11 +282,12 @@ protected static CastToByteNode createCoerce() {
}
@GenerateInline(false) // footprint reduction 48 -> 29
+ @ImportStatic(NativeMemory.class)
abstract static class WriteItemAtNode extends Node {
- public abstract void execute(VirtualFrame frame, PMemoryView self, Object ptr, int offset, Object object);
+ public abstract void execute(VirtualFrame frame, PMemoryView self, long ptr, int offset, Object object);
- @Specialization(guards = "ptr != null")
- static void doNative(VirtualFrame frame, PMemoryView self, Object ptr, int offset, Object object,
+ @Specialization(guards = "ptr != NULLPTR")
+ static void doNative(VirtualFrame frame, PMemoryView self, long ptr, int offset, Object object,
@Bind Node inliningTarget,
@Shared @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib,
@Shared @Cached PackValueNode packValueNode) {
@@ -283,8 +297,8 @@ static void doNative(VirtualFrame frame, PMemoryView self, Object ptr, int offse
packValueNode.execute(frame, inliningTarget, self.getFormat(), self.getFormatString(), object, buffer, offset);
}
- @Specialization(guards = "ptr == null")
- static void doManaged(VirtualFrame frame, PMemoryView self, @SuppressWarnings("unused") Object ptr, int offset, Object object,
+ @Specialization(guards = "ptr == NULLPTR")
+ static void doManaged(VirtualFrame frame, PMemoryView self, @SuppressWarnings("unused") long ptr, int offset, Object object,
@Bind Node inliningTarget,
@Shared @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib,
@Shared @Cached PackValueNode packValueNode) {
@@ -296,10 +310,10 @@ static void doManaged(VirtualFrame frame, PMemoryView self, @SuppressWarnings("u
@ValueType
static class MemoryPointer {
- public Object ptr;
+ public long ptr;
public int offset;
- public MemoryPointer(Object ptr, int offset) {
+ public MemoryPointer(long ptr, int offset) {
this.ptr = ptr;
this.offset = offset;
}
@@ -307,7 +321,6 @@ public MemoryPointer(Object ptr, int offset) {
@ImportStatic(PGuards.class)
abstract static class PointerLookupNode extends Node {
- @Child private CExtNodes.PCallCapiFunction callCapiFunction;
@Child private PyNumberAsSizeNode asSizeNode;
// index can be a tuple, int or int-convertible
@@ -332,7 +345,7 @@ private void lookupDimension(Node inliningTarget, PMemoryView self, MemoryPointe
if (hasSuboffsetsProfile.profile(inliningTarget, suboffsets != null) && suboffsets[dim] >= 0) {
// The length may be out of bounds, but sulong shouldn't care if we don't
// access the out-of-bound part
- ptr.ptr = getCallCapiFunction().call(NativeCAPISymbol.FUN_ADD_SUBOFFSET, ptr.ptr, ptr.offset, suboffsets[dim]);
+ ptr.ptr = addSuboffset(ptr.ptr, ptr.offset, suboffsets[dim]);
ptr.offset = 0;
}
}
@@ -440,13 +453,6 @@ private PyNumberAsSizeNode getAsSizeNode() {
return asSizeNode;
}
- private CExtNodes.PCallCapiFunction getCallCapiFunction() {
- if (callCapiFunction == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- callCapiFunction = insert(CExtNodes.PCallCapiFunction.create());
- }
- return callCapiFunction;
- }
}
@GenerateUncached
@@ -460,14 +466,13 @@ byte[] tobytesCached(PMemoryView self,
@Bind Node inliningTarget,
@Cached("self.getDimensions()") int cachedDimensions,
@Shared @Cached ReadBytesAtNode readBytesAtNode,
- @Shared @Cached CExtNodes.PCallCapiFunction callCapiFunction,
@Shared @Cached PRaiseNode raiseNode) {
self.checkReleased(inliningTarget, raiseNode);
byte[] bytes = new byte[self.getLength()];
if (cachedDimensions == 0) {
readBytesAtNode.execute(inliningTarget, bytes, 0, self.getItemSize(), self, self.getBufferPointer(), self.getOffset());
} else {
- convert(inliningTarget, bytes, self, cachedDimensions, readBytesAtNode, callCapiFunction);
+ convert(inliningTarget, bytes, self, cachedDimensions, readBytesAtNode);
}
return bytes;
}
@@ -476,43 +481,42 @@ byte[] tobytesCached(PMemoryView self,
byte[] tobytesGeneric(PMemoryView self,
@Bind Node inliningTarget,
@Shared @Cached ReadBytesAtNode readBytesAtNode,
- @Shared @Cached CExtNodes.PCallCapiFunction callCapiFunction,
@Shared @Cached PRaiseNode raiseNode) {
self.checkReleased(inliningTarget, raiseNode);
byte[] bytes = new byte[self.getLength()];
if (self.getDimensions() == 0) {
readBytesAtNode.execute(inliningTarget, bytes, 0, self.getItemSize(), self, self.getBufferPointer(), self.getOffset());
} else {
- convertBoundary(inliningTarget, bytes, self, self.getDimensions(), readBytesAtNode, callCapiFunction);
+ convertBoundary(inliningTarget, bytes, self, self.getDimensions(), readBytesAtNode);
}
return bytes;
}
@TruffleBoundary
- private void convertBoundary(Node inliningTarget, byte[] dest, PMemoryView self, int ndim, ReadBytesAtNode readBytesAtNode, CExtNodes.PCallCapiFunction callCapiFunction) {
- convert(inliningTarget, dest, self, ndim, readBytesAtNode, callCapiFunction);
+ private void convertBoundary(Node inliningTarget, byte[] dest, PMemoryView self, int ndim, ReadBytesAtNode readBytesAtNode) {
+ convert(inliningTarget, dest, self, ndim, readBytesAtNode);
}
- protected void convert(Node inliningTarget, byte[] dest, PMemoryView self, int ndim, ReadBytesAtNode readBytesAtNode, CExtNodes.PCallCapiFunction callCapiFunction) {
- recursive(inliningTarget, dest, 0, self, 0, ndim, self.getBufferPointer(), self.getOffset(), readBytesAtNode, callCapiFunction);
+ protected void convert(Node inliningTarget, byte[] dest, PMemoryView self, int ndim, ReadBytesAtNode readBytesAtNode) {
+ recursive(inliningTarget, dest, 0, self, 0, ndim, self.getBufferPointer(), self.getOffset(), readBytesAtNode);
}
- private static int recursive(Node inliningTarget, byte[] dest, int initialDestOffset, PMemoryView self, int dim, int ndim, Object ptr, int initialOffset, ReadBytesAtNode readBytesAtNode,
- CExtNodes.PCallCapiFunction callCapiFunction) {
+ private static int recursive(Node inliningTarget, byte[] dest, int initialDestOffset, PMemoryView self, int dim, int ndim, long ptr, int initialOffset,
+ ReadBytesAtNode readBytesAtNode) {
int offset = initialOffset;
int destOffset = initialDestOffset;
for (int i = 0; i < self.getBufferShape()[dim]; i++) {
- Object xptr = ptr;
+ long xptr = ptr;
int xoffset = offset;
if (self.getBufferSuboffsets() != null && self.getBufferSuboffsets()[dim] >= 0) {
- xptr = callCapiFunction.call(NativeCAPISymbol.FUN_ADD_SUBOFFSET, ptr, offset, self.getBufferSuboffsets()[dim]);
+ xptr = addSuboffset(ptr, offset, self.getBufferSuboffsets()[dim]);
xoffset = 0;
}
if (dim == ndim - 1) {
readBytesAtNode.execute(inliningTarget, dest, destOffset, self.getItemSize(), self, xptr, xoffset);
destOffset += self.getItemSize();
} else {
- destOffset = recursive(inliningTarget, dest, destOffset, self, dim + 1, ndim, xptr, xoffset, readBytesAtNode, callCapiFunction);
+ destOffset = recursive(inliningTarget, dest, destOffset, self, dim + 1, ndim, xptr, xoffset, readBytesAtNode);
}
offset += self.getBufferStrides()[dim];
}
@@ -529,26 +533,25 @@ public static ToJavaBytesNode create() {
@GenerateInline(false)
public abstract static class ToJavaBytesFortranOrderNode extends ToJavaBytesNode {
@Override
- protected void convert(Node inliningTarget, byte[] dest, PMemoryView self, int ndim, ReadBytesAtNode readBytesAtNode, CExtNodes.PCallCapiFunction callCapiFunction) {
- recursive(inliningTarget, dest, 0, self.getItemSize(), self, 0, ndim, self.getBufferPointer(), self.getOffset(), readBytesAtNode, callCapiFunction);
+ protected void convert(Node inliningTarget, byte[] dest, PMemoryView self, int ndim, ReadBytesAtNode readBytesAtNode) {
+ recursive(inliningTarget, dest, 0, self.getItemSize(), self, 0, ndim, self.getBufferPointer(), self.getOffset(), readBytesAtNode);
}
- private static void recursive(Node inliningTarget, byte[] dest, int initialDestOffset, int destStride, PMemoryView self, int dim, int ndim, Object ptr, int initialOffset,
- ReadBytesAtNode readBytesAtNode,
- CExtNodes.PCallCapiFunction callCapiFunction) {
+ private static void recursive(Node inliningTarget, byte[] dest, int initialDestOffset, int destStride, PMemoryView self, int dim, int ndim, long ptr, int initialOffset,
+ ReadBytesAtNode readBytesAtNode) {
int offset = initialOffset;
int destOffset = initialDestOffset;
for (int i = 0; i < self.getBufferShape()[dim]; i++) {
- Object xptr = ptr;
+ long xptr = ptr;
int xoffset = offset;
if (self.getBufferSuboffsets() != null && self.getBufferSuboffsets()[dim] >= 0) {
- xptr = callCapiFunction.call(NativeCAPISymbol.FUN_ADD_SUBOFFSET, ptr, offset, self.getBufferSuboffsets()[dim]);
+ xptr = addSuboffset(ptr, offset, self.getBufferSuboffsets()[dim]);
xoffset = 0;
}
if (dim == ndim - 1) {
readBytesAtNode.execute(inliningTarget, dest, destOffset, self.getItemSize(), self, xptr, xoffset);
} else {
- recursive(inliningTarget, dest, destOffset, destStride * self.getBufferShape()[dim], self, dim + 1, ndim, xptr, xoffset, readBytesAtNode, callCapiFunction);
+ recursive(inliningTarget, dest, destOffset, destStride * self.getBufferShape()[dim], self, dim + 1, ndim, xptr, xoffset, readBytesAtNode);
}
destOffset += destStride;
offset += self.getBufferStrides()[dim];
@@ -613,8 +616,13 @@ public final void execute(VirtualFrame frame, Node inliningTarget, InteropCallDa
@Specialization
static void doCApiCached(NativeBufferLifecycleManager.NativeBufferLifecycleManagerFromType buffer,
- @Cached(inline = false) PCallCapiFunction callReleaseNode) {
- callReleaseNode.call(NativeCAPISymbol.FUN_GRAALPY_RELEASE_BUFFER, buffer.bufferStructPointer);
+ @Bind Node inliningTarget) {
+ try {
+ ExternalFunctionInvoker.invokeGRAALPY_RELEASE_BUFFER(CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_GRAALPY_RELEASE_BUFFER).getAddress(),
+ buffer.bufferStructPointer);
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
}
@Specialization
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/NativeBufferLifecycleManager.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/NativeBufferLifecycleManager.java
index bf71fd862f..2dbcfb35af 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/NativeBufferLifecycleManager.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/NativeBufferLifecycleManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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
@@ -40,6 +40,8 @@
*/
package com.oracle.graal.python.builtins.objects.memoryview;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+
/**
* Object for tracking lifetime of buffers inside memoryviews. The only purpose is to release the
* underlying buffer when this object's export count goes to 0 or it gets garbage collected. Should
@@ -57,10 +59,10 @@ public abstract class NativeBufferLifecycleManager extends BufferLifecycleManage
*/
public static final class NativeBufferLifecycleManagerFromType extends NativeBufferLifecycleManager {
/** Pointer to native Py_buffer */
- final Object bufferStructPointer;
+ final long bufferStructPointer;
- public NativeBufferLifecycleManagerFromType(Object bufferStructPointer) {
- assert bufferStructPointer != null;
+ public NativeBufferLifecycleManagerFromType(long bufferStructPointer) {
+ assert bufferStructPointer != NULLPTR;
this.bufferStructPointer = bufferStructPointer;
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/PMemoryView.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/PMemoryView.java
index a27e636831..326cec3553 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/PMemoryView.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/memoryview/PMemoryView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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
@@ -42,6 +42,7 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.BufferError;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import java.nio.ByteOrder;
import java.util.concurrent.atomic.AtomicLong;
@@ -49,7 +50,6 @@
import com.oracle.graal.python.builtins.objects.buffer.BufferFlags;
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAcquireLibrary;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyMemoryViewWrapper;
import com.oracle.graal.python.builtins.objects.memoryview.MemoryViewNodes.ReleaseBufferNode;
import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject;
import com.oracle.graal.python.nodes.ErrorMessages;
@@ -91,7 +91,7 @@ public final class PMemoryView extends PythonBuiltinObject {
private final int ndim;
// We cannot easily add numbers to pointers in Java, so the actual pointer is bufPointer +
// offset
- private final Object bufPointer;
+ private final long bufPointer;
private final int offset;
private final int[] shape;
private final int[] strides;
@@ -107,7 +107,7 @@ public final class PMemoryView extends PythonBuiltinObject {
private int cachedHash = -1;
public PMemoryView(Object cls, Shape instanceShape, PythonContext context, BufferLifecycleManager bufferLifecycleManager, Object buffer, Object owner,
- int len, boolean readonly, int itemsize, BufferFormat format, TruffleString formatString, int ndim, Object bufPointer,
+ int len, boolean readonly, int itemsize, BufferFormat format, TruffleString formatString, int ndim, long bufPointer,
int offset, int[] shape, int[] strides, int[] suboffsets, int flags) {
super(cls, instanceShape);
PythonBufferAccessLibrary.assertIsBuffer(buffer);
@@ -128,7 +128,6 @@ public PMemoryView(Object cls, Shape instanceShape, PythonContext context, Buffe
if (bufferLifecycleManager != null) {
this.reference = BufferReference.createBufferReference(this, bufferLifecycleManager, context);
}
- setNativeWrapper(new PyMemoryViewWrapper(this));
}
// From CPython init_strides_from_shape
@@ -180,7 +179,7 @@ public int getDimensions() {
return ndim;
}
- public Object getBufferPointer() {
+ public long getBufferPointer() {
return bufPointer;
}
@@ -473,7 +472,7 @@ void writeDoubleByteOrder(int byteOffset, double value, ByteOrder byteOrder,
@ExportMessage
boolean isNative(
@Shared("bufferLib") @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib) {
- if (getBufferPointer() != null) {
+ if (getBufferPointer() != NULLPTR) {
return true;
} else {
return bufferLib.isNative(buffer);
@@ -481,9 +480,9 @@ boolean isNative(
}
@ExportMessage
- Object getNativePointer(
+ long getNativePointer(
@Shared("bufferLib") @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib) {
- if (getBufferPointer() != null) {
+ if (getBufferPointer() != NULLPTR) {
return getBufferPointer();
} else {
return bufferLib.getNativePointer(buffer);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java
index a227db49be..c890a710fa 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java
@@ -89,7 +89,6 @@
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
import com.oracle.truffle.api.strings.TruffleString;
@@ -144,20 +143,14 @@ protected static Object doIt(PBuiltinMethod self) {
@Slot(value = SlotKind.tp_richcompare, isComplex = true)
@GenerateNodeFactory
abstract static class EqNode extends RichCmpBuiltinNode {
-
- @Child private InteropLibrary identicalLib = InteropLibrary.getFactory().createDispatched(3);
- @Child private InteropLibrary identicalLib2 = InteropLibrary.getFactory().createDispatched(3);
-
private boolean eq(Object function1, Object function2, Object self1, Object self2) {
if (function1 != function2) {
return false;
}
if (self1 != self2) {
// CPython compares PyObject* pointers:
- if (self1 instanceof PythonAbstractNativeObject && self2 instanceof PythonAbstractNativeObject) {
- if (identicalLib.isIdentical(((PythonAbstractNativeObject) self1).getPtr(), ((PythonAbstractNativeObject) self2).getPtr(), identicalLib2)) {
- return true;
- }
+ if (self1 instanceof PythonAbstractNativeObject obj1 && self2 instanceof PythonAbstractNativeObject obj2) {
+ return obj1.getPtr() == obj2.getPtr();
}
return false;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/PMMap.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/PMMap.java
index 89766514e4..8f089e14a3 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/PMMap.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/PMMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2026, 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
@@ -40,6 +40,7 @@
*/
package com.oracle.graal.python.builtins.objects.mmap;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
@@ -220,13 +221,13 @@ boolean isNative(
}
@ExportMessage
- Object getNativePointer(
+ long getNativePointer(
@Bind Node inliningTarget,
@Shared @CachedLibrary(limit = "1") PosixSupportLibrary posixLib) {
try {
return posixLib.mmapGetPointer(PythonContext.get(inliningTarget).getPosixSupport(), getPosixSupportHandle());
} catch (PosixSupportLibrary.UnsupportedPosixFeatureException e) {
- return null;
+ return NULLPTR;
}
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java
index 92edd0cf17..617bbe8c11 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java
@@ -53,8 +53,8 @@ public final class PythonModule extends PythonObject {
* Stores the native {@code PyModuleDef *} structure if this module was created via the
* multiphase extension module initialization mechanism.
*/
- private Object nativeModuleDef;
- private Object nativeModuleState;
+ private long nativeModuleDef;
+ private long nativeModuleState;
/**
* Replicates the native references of this module's native state in Java.
@@ -139,19 +139,19 @@ public String toString() {
return "";
}
- public Object getNativeModuleDef() {
+ public long getNativeModuleDef() {
return nativeModuleDef;
}
- public void setNativeModuleDef(Object nativeModuleDef) {
+ public void setNativeModuleDef(long nativeModuleDef) {
this.nativeModuleDef = nativeModuleDef;
}
- public Object getNativeModuleState() {
+ public long getNativeModuleState() {
return nativeModuleState;
}
- public void setNativeModuleState(Object nativeModuleState) {
+ public void setNativeModuleState(long nativeModuleState) {
this.nativeModuleState = nativeModuleState;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java
index 266c15d2ba..1d38f5444a 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java
@@ -55,6 +55,7 @@
import static com.oracle.graal.python.nodes.StringLiterals.T_NONE;
import static com.oracle.graal.python.nodes.StringLiterals.T_SINGLE_QUOTE_COMMA_SPACE;
+import java.lang.ref.Reference;
import java.util.List;
import com.oracle.graal.python.PythonLanguage;
@@ -70,7 +71,10 @@
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.PNotImplemented;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
@@ -144,6 +148,7 @@
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext;
import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
import com.oracle.graal.python.runtime.object.PFactory;
@@ -346,14 +351,26 @@ Object doNativeObjectDirect(VirtualFrame frame, Object self, Object[] varargs, P
@GenerateInline
@GenerateCached(false)
protected abstract static class CallNativeGenericNewNode extends Node {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, FUN_PY_OBJECT_NEW);
+
abstract Object execute(Node inliningTarget, Object cls);
@Specialization
static Object call(Object cls,
+ @Bind Node inliningTarget,
@Cached(inline = false) CApiTransitions.PythonToNativeNode toNativeNode,
- @Cached(inline = false) CApiTransitions.NativeToPythonTransferNode toPythonNode,
- @Cached(inline = false) CExtNodes.PCallCapiFunction callCapiFunction) {
- return toPythonNode.execute(callCapiFunction.call(FUN_PY_OBJECT_NEW, toNativeNode.execute(cls)));
+ @Cached(inline = false) CApiTransitions.NativeToPythonTransferNode toPythonNode) {
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(cls);
+ long clsPointer = toNativeNode.executeLong(cls);
+ try {
+ PythonContext context = PythonContext.get(inliningTarget);
+ var callable = CApiContext.getNativeSymbol(inliningTarget, FUN_PY_OBJECT_NEW);
+ return toPythonNode.execute(ExternalFunctionInvoker.invokePY_OBJECT_NEW(null, C_API_TIMING,
+ context.ensureNativeContext(), BoundaryCallData.getUncached(),
+ context.getThreadState(context.getLanguage(inliningTarget)), callable, clsPointer));
+ } finally {
+ Reference.reachabilityFence(cls);
+ }
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java
index e627c95699..329e0b7af6 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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
@@ -72,6 +72,8 @@
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
+import java.lang.ref.Reference;
+
import org.graalvm.collections.Pair;
import com.oracle.graal.python.PythonLanguage;
@@ -83,8 +85,9 @@
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
import com.oracle.graal.python.builtins.objects.cell.PCell;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
@@ -142,7 +145,6 @@
import com.oracle.graal.python.runtime.object.IDUtils;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
-import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Bind;
@@ -158,8 +160,6 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;
@@ -316,10 +316,8 @@ static Object id(PythonBuiltinClass self) {
}
@Specialization
- static Object id(PythonAbstractNativeObject self,
- @Bind Node inliningTarget,
- @CachedLibrary(limit = "2") InteropLibrary lib) {
- return PythonUtils.coerceToLong(self.getPtr(), lib);
+ static Object id(PythonAbstractNativeObject self) {
+ return self.getPtr();
}
@Specialization
@@ -382,11 +380,6 @@ static Object id(PString self,
return getObjectIdNode.execute(inliningTarget, self);
}
- @Specialization
- Object id(PythonNativeVoidPtr self) {
- return self.getNativePointer();
- }
-
@Specialization
Object id(PCell self) {
return PythonContext.get(this).getNextObjectId(self);
@@ -635,10 +628,19 @@ abstract static class CheckBasesizeForGetState extends Node {
@Specialization
static boolean doNative(@SuppressWarnings("unused") PythonAbstractNativeObject obj, Object type, int slotNum,
- @Cached(inline = false) PythonToNativeNode toSulongNode,
- @Cached(inline = false) CExtNodes.PCallCapiFunction callCapiFunction) {
- Object result = callCapiFunction.call(FUN_CHECK_BASICSIZE_FOR_GETSTATE, toSulongNode.execute(type), slotNum);
- return (int) result == 0;
+ @Bind Node inliningTarget,
+ @Cached(inline = false) PythonToNativeNode toNativeNode) {
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(type);
+ long typePointer = toNativeNode.executeLong(type);
+ try {
+ return ExternalFunctionInvoker.invokeCHECK_BASICSIZE_FOR_GETSTATE(
+ CApiContext.getNativeSymbol(inliningTarget, FUN_CHECK_BASICSIZE_FOR_GETSTATE).getAddress(), typePointer,
+ slotNum) == 0;
+ } catch (Throwable t) {
+ throw com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere(t);
+ } finally {
+ Reference.reachabilityFence(type);
+ }
}
@Fallback
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java
index 3f9bb0d243..c52b0edc05 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java
@@ -33,6 +33,9 @@
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonObjectReference;
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
@@ -43,6 +46,7 @@
import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
@@ -78,8 +82,19 @@ public class PythonObject extends PythonAbstractObject {
*/
public static final byte IS_STATIC_BASE = 0b10000;
+ /**
+ * Reference count of an object that is only referenced by the Java heap - this is larger than 1
+ * since native code sometimes special cases for low refcounts.
+ */
+ public static final long MANAGED_REFCNT = 10;
+
+ public static final long IMMORTAL_REFCNT = 0xFFFFFFFFL; // from include/object.h
+
private Object pythonClass;
+ private long nativePointer = UNINITIALIZED;
+ public PythonObjectReference ref;
+
@SuppressWarnings("this-escape") // escapes in the assertion
public PythonObject(Object pythonClass, Shape instanceShape) {
super(instanceShape);
@@ -169,4 +184,56 @@ public String toString() {
public static int getCallSiteInlineCacheMaxDepth() {
return PythonOptions.getCallSiteInlineCacheMaxDepth();
}
+
+ public final long getNativePointer() {
+ return nativePointer;
+ }
+
+ public final void setNativePointer(long nativePointer) {
+ assert nativePointer != UNINITIALIZED;
+ // we should set the pointer just once
+ assert this.nativePointer == UNINITIALIZED || this.nativePointer == nativePointer;
+ this.nativePointer = nativePointer;
+ }
+
+ public final void clearNativePointer() {
+ this.nativePointer = UNINITIALIZED;
+ }
+
+ @InliningCutoff
+ public final boolean isNative() {
+ return nativePointer != UNINITIALIZED;
+ }
+
+ public final long getRefCount() {
+ if (isNative()) {
+ return CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(nativePointer));
+ }
+ return MANAGED_REFCNT;
+ }
+
+ public final long incRef() {
+ assert isNative();
+ long pointer = HandlePointerConverter.pointerToStub(nativePointer);
+ long refCount = CApiTransitions.readNativeRefCount(pointer);
+ assert refCount >= MANAGED_REFCNT : "invalid refcnt " + refCount + " during incRef in " + Long.toHexString(nativePointer);
+ if (refCount != IMMORTAL_REFCNT) {
+ CApiTransitions.writeNativeRefCount(pointer, refCount + 1);
+ return refCount + 1;
+ }
+ return IMMORTAL_REFCNT;
+ }
+
+ public final long decRef() {
+ assert isNative();
+ long pointer = HandlePointerConverter.pointerToStub(nativePointer);
+ long refCount = CApiTransitions.readNativeRefCount(pointer);
+ if (refCount != IMMORTAL_REFCNT) {
+ long updatedRefCount = refCount - 1;
+ CApiTransitions.writeNativeRefCount(pointer, updatedRefCount);
+ assert updatedRefCount >= MANAGED_REFCNT : "invalid refcnt " + updatedRefCount + " during decRef in " + Long.toHexString(nativePointer);
+ return updatedRefCount;
+ }
+ return refCount;
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/ReferenceTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/ReferenceTypeBuiltins.java
index 85d62d7bb8..03494c1aed 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/ReferenceTypeBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/referencetype/ReferenceTypeBuiltins.java
@@ -42,6 +42,7 @@
import static com.oracle.graal.python.builtins.objects.PythonAbstractObject.objectHashCode;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_weaklistoffset;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
import static com.oracle.graal.python.nodes.HiddenAttr.WEAKLIST;
import static com.oracle.graal.python.nodes.HiddenAttr.WEAK_REF_QUEUE;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__;
@@ -66,7 +67,6 @@
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.referencetype.ReferenceTypeBuiltinsFactory.ReferenceTypeNodeFactory;
import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode;
import com.oracle.graal.python.builtins.objects.type.PythonClass;
@@ -120,7 +120,6 @@ protected List extends NodeFactory extends PythonBuiltinBaseNode>> getNodeFa
@SlotSignature(name = "ReferenceType", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 3, takesVarKeywordArgs = true)
@GenerateNodeFactory
public abstract static class ReferenceTypeNode extends PythonBuiltinNode {
- @Child private CStructAccess.ReadI64Node getTpWeaklistoffsetNode;
public abstract PReferenceType execute(Object cls, Object object, Object callback);
@@ -190,11 +189,7 @@ PReferenceType refType(Object cls, PythonAbstractNativeObject pythonObject, Obje
if (PGuards.isNativeClass(clazz) || clazz instanceof PythonClass && ((PythonClass) clazz).needsNativeAllocation()) {
for (Object base : getMroNode.execute(inliningTarget, clazz)) {
if (PGuards.isNativeClass(base)) {
- if (getTpWeaklistoffsetNode == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- getTpWeaklistoffsetNode = insert(CStructAccess.ReadI64Node.create());
- }
- long tpWeaklistoffset = getTpWeaklistoffsetNode.readFromObj((PythonNativeClass) base, PyTypeObject__tp_weaklistoffset);
+ long tpWeaklistoffset = readLongField(((PythonNativeClass) base).getPtr(), PyTypeObject__tp_weaklistoffset);
if (tpWeaklistoffset != 0) {
allowed = true;
break;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/NativeStringData.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/NativeStringData.java
index c1ca2d8d36..8954fcec46 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/NativeStringData.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/NativeStringData.java
@@ -60,7 +60,7 @@ private NativeStringData(int charSize, boolean isAscii, NativeByteSequenceStorag
this.storage = storage;
}
- public static NativeStringData create(int charSize, boolean isAscii, Object ptr, int length) {
+ public static NativeStringData create(int charSize, boolean isAscii, long ptr, int length) {
return new NativeStringData(charSize, isAscii, NativeByteSequenceStorage.create(ptr, length, length, true));
}
@@ -72,7 +72,7 @@ public int getCharSize() {
return kind != 0 ? kind : KIND_1BYTE;
}
- public Object getPtr() {
+ public long getPtr() {
return storage.getPtr();
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java
index 212aadf93b..34d2c45953 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java
@@ -319,13 +319,13 @@ static Object doBuffer(VirtualFrame frame, Object cls, Object obj, Object encodi
static Object doNativeSubclass(VirtualFrame frame, Object cls, Object obj, @SuppressWarnings("unused") Object encoding, @SuppressWarnings("unused") Object errors,
@SuppressWarnings("unused") @Bind Node inliningTarget,
@SuppressWarnings("unused") @Exclusive @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode,
- @Shared @Cached @SuppressWarnings("unused") IsSubtypeNode isSubtype,
+ @Exclusive @Cached @SuppressWarnings("unused") IsSubtypeNode isSubtype,
@Exclusive @Cached PyObjectStrAsObjectNode strNode,
- @Shared @Cached(neverDefault = true) CExtNodes.StringSubtypeNew subtypeNew) {
+ @Exclusive @Cached CExtNodes.StringSubtypeNew subtypeNew) {
if (obj == PNone.NO_VALUE) {
- return subtypeNew.call(cls, T_EMPTY_STRING);
+ return subtypeNew.execute(inliningTarget, cls, T_EMPTY_STRING);
} else {
- return subtypeNew.call(cls, strNode.execute(frame, inliningTarget, obj));
+ return subtypeNew.execute(inliningTarget, cls, strNode.execute(frame, inliningTarget, obj));
}
}
@@ -335,15 +335,15 @@ static Object doNativeSubclassEncodeErr(VirtualFrame frame, Object cls, Object o
@SuppressWarnings("unused") @Bind Node inliningTarget,
@Exclusive @Cached("createFor($node)") InteropCallData callData,
@SuppressWarnings("unused") @Exclusive @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode,
- @Shared @Cached @SuppressWarnings("unused") IsSubtypeNode isSubtype,
+ @Exclusive @Cached @SuppressWarnings("unused") IsSubtypeNode isSubtype,
@Exclusive @Cached IsBuiltinClassExactProfile isPrimitiveProfile,
@Exclusive @Cached InlinedConditionProfile isStringProfile,
@Exclusive @Cached InlinedConditionProfile isPStringProfile,
@Exclusive @CachedLibrary("obj") PythonBufferAcquireLibrary acquireLib,
@Exclusive @CachedLibrary(limit = "1") PythonBufferAccessLibrary bufferLib,
@Exclusive @Cached BytesCommonBuiltins.DecodeNode decodeNode,
- @Shared @Cached(neverDefault = true) CExtNodes.StringSubtypeNew subtypeNew,
- @Shared @Cached TypeNodes.GetInstanceShape getInstanceShape,
+ @Exclusive @Cached CExtNodes.StringSubtypeNew subtypeNew,
+ @Exclusive @Cached TypeNodes.GetInstanceShape getInstanceShape,
@Exclusive @Cached PRaiseNode raiseNode) {
Object buffer;
try {
@@ -356,9 +356,9 @@ static Object doNativeSubclassEncodeErr(VirtualFrame frame, Object cls, Object o
Object en = encoding == PNone.NO_VALUE ? T_UTF8 : encoding;
Object result = assertNoJavaString(decodeNode.execute(frame, bytesObj, en, errors));
if (isStringProfile.profile(inliningTarget, result instanceof TruffleString)) {
- return subtypeNew.call(cls, asPString(cls, (TruffleString) result, inliningTarget, isPrimitiveProfile, getInstanceShape));
+ return subtypeNew.execute(inliningTarget, cls, asPString(cls, (TruffleString) result, inliningTarget, isPrimitiveProfile, getInstanceShape));
} else if (isPStringProfile.profile(inliningTarget, result instanceof PString)) {
- return subtypeNew.call(cls, result);
+ return subtypeNew.execute(inliningTarget, cls, result);
}
throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.P_S_RETURNED_NON_STRING, bytesObj, "decode", result);
} finally {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java
index 2d40036a59..b288cf18bb 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java
@@ -53,8 +53,11 @@
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.bytes.BytesUtils;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.common.SequenceNodes;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
@@ -76,6 +79,8 @@
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToJavaStringNode;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.sequence.PSequence;
@@ -137,6 +142,7 @@ static TruffleString doNative(Node inliningTarget, PString x,
@ImportStatic(StringNodes.class)
@GenerateInline(false) // footprint reduction 40 -> 21
public abstract static class StringLenNode extends PNodeWithContext {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_PY_UNICODE_GET_LENGTH);
public abstract int execute(Object str);
@@ -160,11 +166,14 @@ static int doNative(PString x,
@Cached StringMaterializeNode materializeNode,
@Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode) {
NativeStringData nativeData = x.getNativeStringData(inliningTarget, readAttrNode);
- if (oneByteProfile.profile(inliningTarget, nativeData.getCharSize() == 1)) {
- return nativeData.length();
- } else {
- return doString(materializeNode.execute(inliningTarget, x), codePointLengthNode);
- }
+ int charSize = nativeData.getCharSize();
+ assert charSize == 1 || charSize == 2 || charSize == 4;
+ /*
+ * Avoid materialization of strings backed by native memory. It is correct to use the
+ * 'length / charSize' because native data always contains characters with fixed byte
+ * size (i.e. Py_UCS1, Py_UCS2, or Py_UCS4).
+ */
+ return nativeData.length() / nativeData.getCharSize();
}
@Specialization
@@ -173,14 +182,17 @@ static int doNativeObject(PythonNativeObject x,
@Bind Node inliningTarget,
@Cached GetClassNode getClassNode,
@Cached IsSubtypeNode isSubtypeNode,
- @Cached PCallCapiFunction callNativeUnicodeAsStringNode,
- @Cached PythonToNativeNode toSulongNode,
+ @Cached PythonToNativeNode toNativeNode,
@Cached PRaiseNode raiseNode) {
if (isSubtypeNode.execute(getClassNode.execute(inliningTarget, x), PythonBuiltinClassType.PString)) {
// read the native data
- Object result = callNativeUnicodeAsStringNode.call(NativeCAPISymbol.FUN_PY_UNICODE_GET_LENGTH, toSulongNode.execute(x));
- assert result instanceof Number;
- return intValue((Number) result);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(x);
+ PythonContext context = PythonContext.get(inliningTarget);
+ var callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_PY_UNICODE_GET_LENGTH);
+ return intValue(ExternalFunctionInvoker.invokePY_UNICODE_GET_LENGTH(null, C_API_TIMING, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(),
+ context.getThreadState(context.getLanguage(inliningTarget)), callable,
+ toNativeNode.executeLong(x)));
}
// the object's type is not a subclass of 'str'
throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.BAD_ARG_TYPE_FOR_BUILTIN_OP);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/PStruct.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/PStruct.java
index 21288017e5..70f991cf14 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/PStruct.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/struct/PStruct.java
@@ -1,4 +1,4 @@
-/* Copyright (c) 2020, 2024, Oracle and/or its affiliates.
+/* Copyright (c) 2020, 2025, Oracle and/or its affiliates.
* Copyright (C) 1996-2020 Python Software Foundation
*
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -56,10 +56,6 @@ public boolean isBigEndian() {
return formatAlignment.bigEndian;
}
- public boolean isNative() {
- return formatAlignment.nativeSizing;
- }
-
@ValueType
public record StructInfo(byte[] format, int size, int len, FormatAlignment formatAlignment, FormatCode[] codes) {
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/CapsuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/CapsuleBuiltins.java
index bde5fb8490..d8c301a6a0 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/CapsuleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/CapsuleBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -40,6 +40,8 @@
*/
package com.oracle.graal.python.builtins.objects.tuple;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readByteArrayElement;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import java.util.Collections;
@@ -52,7 +54,6 @@
import com.oracle.graal.python.builtins.PythonBuiltins;
import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
@@ -60,7 +61,6 @@
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.strings.TruffleString;
@CoreFunctions(extendClasses = PythonBuiltinClassType.Capsule)
@@ -81,13 +81,13 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode {
@TruffleBoundary
static Object repr(PyCapsule self) {
String name;
- if (self.getNamePtr() == null || InteropLibrary.getUncached().isNull(self.getNamePtr())) {
+ if (self.getNamePtr() == NULLPTR) {
name = "NULL";
} else {
StringBuilder builder = new StringBuilder("\"");
int i = 0;
byte b;
- while ((b = CStructAccess.ReadByteNode.getUncached().readArrayElement(self.getNamePtr(), i++)) != 0) {
+ while ((b = readByteArrayElement(self.getNamePtr(), i++)) != 0) {
builder.append((char) b);
}
builder.append('"');
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequence.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequence.java
index a8390b41f0..537008db32 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequence.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequence.java
@@ -204,7 +204,7 @@ public static void initType(PythonContext context, PythonAbstractClass klass, De
* about tp_new in TpSlots.updateSlots. We have to write it manually
*/
nativeClass.setTpSlots(nativeClass.getTpSlots().copy().set(TpSlots.TpSlotMeta.TP_NEW, newSlot).build());
- TpSlots.toNative(nativeClass.getPtr(), TpSlots.TpSlotMeta.TP_NEW, newSlot, context.getNativeNull());
+ TpSlots.toNative(nativeClass.getPtr(), TpSlots.TpSlotMeta.TP_NEW, newSlot);
TpSlotBuiltin> reprSlot = (TpSlotBuiltin>) StructSequenceBuiltins.SLOTS.tp_repr();
writeAttrNode.execute(klass, T___REPR__, reprSlot.createBuiltin(context, klass, T___REPR__, TpSlots.TpSlotMeta.TP_REPR.getNativeSignature()));
PythonBuiltinClass template = context.lookupType(PythonBuiltinClassType.PFloatInfo);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java
index 8ecf4975b3..847f2cc81b 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java
@@ -40,6 +40,9 @@
*/
package com.oracle.graal.python.builtins.objects.tuple;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayPtrField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__;
import static com.oracle.graal.python.nodes.StringLiterals.T_COMMA_SPACE;
import static com.oracle.graal.python.nodes.StringLiterals.T_EQ;
@@ -61,7 +64,6 @@
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.object.ObjectNodes;
@@ -72,7 +74,6 @@
import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.HiddenAttr;
-import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
@@ -93,7 +94,6 @@
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.strings.TruffleStringBuilder;
@@ -167,14 +167,12 @@ static TruffleString[] doManaged(Node inliningTarget, PythonManagedClass type,
@Specialization
@TruffleBoundary
static TruffleString[] doNative(PythonAbstractNativeObject type) {
- CStructAccess.ReadPointerNode read = CStructAccess.ReadPointerNode.getUncached();
- Object membersPtr = read.readFromObj(type, CFields.PyTypeObject__tp_members);
+ long membersPtr = readPtrField(type.getPtr(), CFields.PyTypeObject__tp_members);
List members = new ArrayList<>();
- InteropLibrary lib = InteropLibrary.getUncached();
- if (!PGuards.isNullOrZero(membersPtr, lib)) {
+ if (membersPtr != NULLPTR) {
for (int i = 0;; i++) {
- Object memberNamePtr = read.readStructArrayElement(membersPtr, i, CFields.PyMemberDef__name);
- if (PGuards.isNullOrZero(memberNamePtr, lib)) {
+ long memberNamePtr = readStructArrayPtrField(membersPtr, i, CFields.PyMemberDef__name);
+ if (memberNamePtr == NULLPTR) {
break;
}
TruffleString name = CExtNodes.FromCharPointerNode.executeUncached(memberNamePtr);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java
index 99b702cbf2..f37cabf139 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/TupleBuiltins.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2025, Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2026, Oracle and/or its affiliates.
* Copyright (c) 2014, Regents of the University of California
*
* All rights reserved.
@@ -145,7 +145,7 @@ static Object doGeneric(VirtualFrame frame, Object cls, Object iterable,
} else if (subtypeNode.execute(cls, PythonBuiltinClassType.PTuple)) {
if (needsNativeAllocationNode.execute(inliningTarget, cls)) {
// delegate to tuple_subtype_new(PyTypeObject *type, PyObject *x)
- return subtypeNew.call(cls, iterable);
+ return subtypeNew.execute(inliningTarget, cls, iterable);
} else {
PTuple tuple = constructTupleNode.execute(frame, iterable);
return PFactory.createTuple(cls, getInstanceShape.execute(cls), tuple.getSequenceStorage());
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonAbstractClass.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonAbstractClass.java
index 644137c98c..25744ee689 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonAbstractClass.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonAbstractClass.java
@@ -40,20 +40,21 @@
*/
package com.oracle.graal.python.builtins.objects.type;
+import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
import com.oracle.truffle.api.interop.TruffleObject;
public interface PythonAbstractClass extends TruffleObject {
- public static final PythonAbstractClass[] EMPTY_ARRAY = {};
+ PythonAbstractClass[] EMPTY_ARRAY = {};
static boolean isInstance(Object object) {
return PythonManagedClass.isInstance(object) || PythonNativeClass.isInstance(object);
}
static PythonAbstractClass cast(Object object) {
- if (PythonNativeClass.isInstance(object)) {
- return PythonNativeClass.cast(object);
+ if (object instanceof PythonAbstractNativeObject nativeObject) {
+ return nativeObject;
} else {
return PythonManagedClass.cast(object);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonManagedClass.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonManagedClass.java
index 9465373571..3fd214babc 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonManagedClass.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonManagedClass.java
@@ -31,10 +31,11 @@
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonClassNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
import com.oracle.graal.python.builtins.objects.dict.PDict;
@@ -284,8 +285,14 @@ private void unsafeSetSuperClass(PythonAbstractClass... newBaseClasses) {
for (PythonAbstractClass base : getBaseClasses()) {
if (base != null) {
if (PGuards.isNativeClass(base)) {
- Object nativeBase = PythonToNativeNodeGen.getUncached().execute(base);
- PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_TRUFFLE_CHECK_TYPE_READY, nativeBase);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(base);
+ try {
+ ExternalFunctionInvoker.invokeTRUFFLE_CHECK_TYPE_READY(
+ CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_TRUFFLE_CHECK_TYPE_READY).getAddress(),
+ PythonToNativeNode.executeLongUncached(base));
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
+ }
}
GetSubclassesNode.addSubclass(base, this);
}
@@ -368,10 +375,6 @@ public final PythonAbstractClass[] getBaseClasses() {
return baseClasses;
}
- public PythonClassNativeWrapper getClassNativeWrapper() {
- return (PythonClassNativeWrapper) super.getNativeWrapper();
- }
-
public boolean needsNativeAllocation() {
return needsNativeAllocation;
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java
index 822379578b..d0d730189b 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java
@@ -40,6 +40,9 @@
*/
package com.oracle.graal.python.builtins.objects.type;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writePtrField;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___ABS__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___ADD__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___AITER__;
@@ -119,7 +122,6 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___TRUEDIV__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___XOR__;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
-import static com.oracle.graal.python.util.PythonUtils.getUncachedInterop;
import java.util.ArrayList;
import java.util.Arrays;
@@ -135,38 +137,35 @@
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.BinaryOpSlotFuncWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.BinarySlotFuncWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.CallWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.DescrGetFunctionWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.DescrSetFunctionWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.GetAttrWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.HashfuncWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.InitWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.InquiryWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.IterNextWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.LenfuncWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.NbInPlacePowerWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.NbPowerWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.NewWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.ObjobjargWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.RichcmpFunctionWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.SetattrWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.SqContainsWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.SsizeargfuncSlotWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.SsizeobjargprocWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.TpSlotWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.UnaryFuncWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonClassNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.EnsureExecutableNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.BinaryOpSlotFuncWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.BinarySlotFuncWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.CallWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.DescrGetFunctionWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.DescrSetFunctionWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.GetAttrWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.HashfuncWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.InitWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.InquiryWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.IterNextWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.LenfuncWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.NbInPlacePowerWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.NbPowerWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.NewWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.ObjobjargWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.RichcmpFunctionWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.SetAttrWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.SqContainsWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.SsizeargfuncSlotWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.SsizeobjargprocWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper.UnaryFuncWrapper;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.ReadPointerNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.WritePointerNode;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod;
@@ -211,6 +210,7 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.TpSlotVarargsBuiltin;
import com.oracle.graal.python.lib.PyDictGetItem;
import com.oracle.graal.python.lib.PyDictSetItem;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
@@ -231,8 +231,6 @@
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.DenyReplace;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.UnadoptableNode;
@@ -275,10 +273,10 @@
* - it has some quirks, so we follow the same algorithm using the the slotdefs ({@link #SLOTDEFS}).
* - This is not called for native types: nor static, neither heap types
* - When Python class goes to native (in ToNativeTypeNode) we convert the slots to native in
- * {@link TpSlot#toNative(TpSlotMeta, TpSlot, Object)}
+ * {@link TpSlot#toNative(TpSlotMeta, TpSlot, long)}
* - TpSlotNative slots are unwrapped
- * - For managed slots we create corresponding {@link PyProcsWrapper}
- * - when {@link PyProcsWrapper} goes to native, it registers itself in a map in context, so
+ * - For managed slots we create corresponding {@link TpSlotWrapper}
+ * - when {@link TpSlotWrapper} goes to native, it registers itself in a map in context, so
* that when it comes back from native in {@link #fromNative(PythonAbstractNativeObject, PythonContext)}
* we can recognize it and use the managed TpSlot object.
*
@@ -449,8 +447,8 @@ public boolean getValue(TpSlots slots) {
}
public boolean readFromNative(PythonAbstractNativeObject pythonClass) {
- Object ptr = ReadPointerNode.getUncached().readFromObj(pythonClass, cField);
- return !InteropLibrary.getUncached().isNull(ptr);
+ long ptr = readPtrField(pythonClass.getPtr(), cField);
+ return ptr != NULLPTR;
}
}
@@ -469,7 +467,7 @@ public enum TpSlotMeta {
TpSlotInquiryBuiltin.class,
TpSlotGroup.AS_NUMBER,
CFields.PyNumberMethods__nb_bool,
- PExternalFunctionWrapper.INQUIRY,
+ PExternalFunctionWrapper.INQUIRYPRED,
InquiryWrapper::new),
NB_INDEX(
TpSlots::nb_index,
@@ -765,7 +763,7 @@ public enum TpSlotMeta {
TpSlotSizeArgFunBuiltin.class,
TpSlotGroup.AS_SEQUENCE,
CFields.PySequenceMethods__sq_item,
- PExternalFunctionWrapper.GETITEM,
+ PExternalFunctionWrapper.SQ_ITEM,
SsizeargfuncSlotWrapper::new),
SQ_ASS_ITEM(
TpSlots::sq_ass_item,
@@ -773,7 +771,7 @@ public enum TpSlotMeta {
TpSlotSqAssItemBuiltin.class,
TpSlotGroup.AS_SEQUENCE,
CFields.PySequenceMethods__sq_ass_item,
- PExternalFunctionWrapper.SETITEM,
+ PExternalFunctionWrapper.SQ_SETITEM,
SsizeobjargprocWrapper::new),
SQ_REPEAT(
TpSlots::sq_repeat,
@@ -781,7 +779,7 @@ public enum TpSlotMeta {
TpSlotSizeArgFunBuiltin.class,
TpSlotGroup.AS_SEQUENCE,
CFields.PySequenceMethods__sq_repeat,
- PExternalFunctionWrapper.SSIZE_ARG,
+ PExternalFunctionWrapper.INDEXARGFUNC,
SsizeargfuncSlotWrapper::new),
SQ_INPLACE_CONCAT(
TpSlots::sq_inplace_concat,
@@ -797,7 +795,7 @@ public enum TpSlotMeta {
TpSlotSizeArgFunBuiltin.class,
TpSlotGroup.AS_SEQUENCE,
CFields.PySequenceMethods__sq_inplace_repeat,
- PExternalFunctionWrapper.SSIZE_ARG,
+ PExternalFunctionWrapper.INDEXARGFUNC,
SsizeargfuncSlotWrapper::new),
SQ_CONTAINS(
TpSlots::sq_contains,
@@ -886,7 +884,7 @@ public enum TpSlotMeta {
TpSlotGroup.NO_GROUP,
CFields.PyTypeObject__tp_setattro,
PExternalFunctionWrapper.SETATTRO,
- SetattrWrapper::new),
+ SetAttrWrapper::new),
TP_SETATTR(
TpSlots::tp_setattr,
null,
@@ -933,7 +931,7 @@ public enum TpSlotMeta {
TpSlotVarargsBuiltin.class,
TpSlotGroup.NO_GROUP,
CFields.PyTypeObject__tp_init,
- PExternalFunctionWrapper.INITPROC,
+ PExternalFunctionWrapper.INIT,
InitWrapper::new),
TP_NEW(
TpSlots::tp_new,
@@ -1039,11 +1037,11 @@ public TpSlot getValue(TpSlots slots) {
return getter.get(slots);
}
- public Object getNativeValue(TpSlots slots, Object defaultValue) {
+ public long getNativeValue(TpSlots slots, long defaultValue) {
return TpSlot.toNative(this, getter.get(slots), defaultValue);
}
- private Object getNativeValue(TpSlot slot, Object defaultValue) {
+ private long getNativeValue(TpSlot slot, long defaultValue) {
return TpSlot.toNative(this, slot, defaultValue);
}
@@ -1051,20 +1049,15 @@ private Object getNativeValue(TpSlot slot, Object defaultValue) {
* Returns Java {@code null} if the native value is NULL, otherwise interop object
* representing the native value.
*/
- public Object readFromNative(PythonAbstractNativeObject pythonClass) {
- Object field = ReadPointerNode.getUncached().readFromObj(pythonClass, nativeGroupOrField);
- InteropLibrary ptrInterop = null;
+ public long readFromNative(PythonAbstractNativeObject pythonClass) {
+ long field = readPtrField(pythonClass.getPtr(), nativeGroupOrField);
if (nativeField != null) {
- ptrInterop = InteropLibrary.getUncached(field);
- if (!ptrInterop.isNull(field)) {
- field = ReadPointerNode.getUncached().read(field, nativeField);
+ if (field != NULLPTR) {
+ field = readPtrField(field, nativeField);
} else {
- return null;
+ return NULLPTR;
}
}
- if (getUncachedInterop(ptrInterop, field).isNull(field)) {
- return null;
- }
return field;
}
@@ -1158,7 +1151,7 @@ private static void addSlotDef(LinkedHashMap defs, TpSl
addSlotDef(s, TpSlotMeta.TP_ITERNEXT, TpSlotDef.withSimpleFunction(T___NEXT__, PExternalFunctionWrapper.ITERNEXT));
addSlotDef(s, TpSlotMeta.TP_STR, TpSlotDef.withSimpleFunction(T___STR__, PExternalFunctionWrapper.UNARYFUNC));
addSlotDef(s, TpSlotMeta.TP_REPR, TpSlotDef.withSimpleFunction(T___REPR__, PExternalFunctionWrapper.UNARYFUNC));
- addSlotDef(s, TpSlotMeta.TP_INIT, TpSlotDef.withSimpleFunction(T___INIT__, PExternalFunctionWrapper.INITPROC));
+ addSlotDef(s, TpSlotMeta.TP_INIT, TpSlotDef.withSimpleFunction(T___INIT__, PExternalFunctionWrapper.INIT));
addSlotDef(s, TpSlotMeta.TP_NEW, TpSlotDef.withSimpleFunction(T___NEW__, PExternalFunctionWrapper.NEW));
addSlotDef(s, TpSlotMeta.TP_CALL, TpSlotDef.withSimpleFunction(T___CALL__, PExternalFunctionWrapper.CALL));
addSlotDef(s, TpSlotMeta.NB_ADD,
@@ -1216,7 +1209,7 @@ private static void addSlotDef(LinkedHashMap defs, TpSl
addSlotDef(s, TpSlotMeta.NB_INPLACE_TRUE_DIVIDE, TpSlotDef.withSimpleFunction(T___ITRUEDIV__, PExternalFunctionWrapper.BINARYFUNC));
addSlotDef(s, TpSlotMeta.NB_INPLACE_MATRIX_MULTIPLY, TpSlotDef.withSimpleFunction(T___IMATMUL__, PExternalFunctionWrapper.BINARYFUNC));
addSlotDef(s, TpSlotMeta.NB_INPLACE_POWER, TpSlotDef.withSimpleFunction(T___IPOW__, PExternalFunctionWrapper.TERNARYFUNC));
- addSlotDef(s, TpSlotMeta.NB_BOOL, TpSlotDef.withSimpleFunction(T___BOOL__, PExternalFunctionWrapper.INQUIRY));
+ addSlotDef(s, TpSlotMeta.NB_BOOL, TpSlotDef.withSimpleFunction(T___BOOL__, PExternalFunctionWrapper.INQUIRYPRED));
addSlotDef(s, TpSlotMeta.NB_INDEX, TpSlotDef.withSimpleFunction(T___INDEX__, PExternalFunctionWrapper.UNARYFUNC));
addSlotDef(s, TpSlotMeta.NB_INT, TpSlotDef.withSimpleFunction(T___INT__, PExternalFunctionWrapper.UNARYFUNC));
addSlotDef(s, TpSlotMeta.NB_FLOAT, TpSlotDef.withSimpleFunction(T___FLOAT__, PExternalFunctionWrapper.UNARYFUNC));
@@ -1236,14 +1229,14 @@ private static void addSlotDef(LinkedHashMap defs, TpSl
// see test_sq_repeat_mul_without_rmul_inheritance
addSlotDef(s, TpSlotMeta.SQ_CONCAT, TpSlotDef.withNoFunction(T___ADD__, PExternalFunctionWrapper.BINARYFUNC));
addSlotDef(s, TpSlotMeta.SQ_REPEAT,
- TpSlotDef.withNoFunction(T___MUL__, PExternalFunctionWrapper.SSIZE_ARG),
- TpSlotDef.withNoFunction(T___RMUL__, PExternalFunctionWrapper.SSIZE_ARG));
- addSlotDef(s, TpSlotMeta.SQ_ITEM, TpSlotDef.withSimpleFunction(T___GETITEM__, PExternalFunctionWrapper.GETITEM));
+ TpSlotDef.withNoFunction(T___MUL__, PExternalFunctionWrapper.INDEXARGFUNC),
+ TpSlotDef.withNoFunction(T___RMUL__, PExternalFunctionWrapper.INDEXARGFUNC));
+ addSlotDef(s, TpSlotMeta.SQ_ITEM, TpSlotDef.withSimpleFunction(T___GETITEM__, PExternalFunctionWrapper.SQ_ITEM));
addSlotDef(s, TpSlotMeta.SQ_ASS_ITEM,
- TpSlotDef.create(T___SETITEM__, TpSlotSqAssItemPython::create, PExternalFunctionWrapper.SETITEM),
- TpSlotDef.create(T___DELITEM__, TpSlotSqAssItemPython::create, PExternalFunctionWrapper.DELITEM));
+ TpSlotDef.create(T___SETITEM__, TpSlotSqAssItemPython::create, PExternalFunctionWrapper.SQ_SETITEM),
+ TpSlotDef.create(T___DELITEM__, TpSlotSqAssItemPython::create, PExternalFunctionWrapper.SQ_DELITEM));
addSlotDef(s, TpSlotMeta.SQ_INPLACE_CONCAT, TpSlotDef.withNoFunction(T___IADD__, PExternalFunctionWrapper.BINARYFUNC));
- addSlotDef(s, TpSlotMeta.SQ_INPLACE_REPEAT, TpSlotDef.withNoFunction(T___IMUL__, PExternalFunctionWrapper.SSIZE_ARG));
+ addSlotDef(s, TpSlotMeta.SQ_INPLACE_REPEAT, TpSlotDef.withNoFunction(T___IMUL__, PExternalFunctionWrapper.INDEXARGFUNC));
addSlotDef(s, TpSlotMeta.SQ_CONTAINS, TpSlotDef.withSimpleFunction(T___CONTAINS__, PExternalFunctionWrapper.OBJOBJPROC));
addSlotDef(s, TpSlotMeta.AM_AWAIT, TpSlotDef.withSimpleFunction(T___AWAIT__, PExternalFunctionWrapper.UNARYFUNC));
addSlotDef(s, TpSlotMeta.AM_ANEXT, TpSlotDef.withSimpleFunction(T___ANEXT__, PExternalFunctionWrapper.UNARYFUNC));
@@ -1279,42 +1272,32 @@ public static TpSlots fromNative(PythonAbstractNativeObject pythonClass, PythonC
if (!def.hasNativeWrapperFactory()) {
continue;
}
- Object field = def.readFromNative(pythonClass);
- if (field == null) {
+ long fieldPtr = def.readFromNative(pythonClass);
+ if (fieldPtr == NULLPTR) {
continue;
}
// Is this pointer representing some TpSlot that we transferred to native?
- InteropLibrary interop = InteropLibrary.getUncached(field);
TpSlotWrapper existingSlotWrapper = null;
- if (interop.isPointer(field)) {
- try {
- long fieldPointer = interop.asPointer(field);
- Object executable = ctx.getCApiContext().getClosureExecutable(fieldPointer);
- if (executable instanceof TpSlotWrapper execWrapper) {
- existingSlotWrapper = execWrapper;
- } else if (executable != null) {
- // This can happen for legacy slots where the delegate would be a PFunction
- LOGGER.fine(() -> String.format("Unexpected executable for slot pointer: %s", executable));
- } else if (def == TpSlotMeta.TP_HASH) {
- // If the slot is not tp_iternext, but the value is
- // PyObject_HashNotImplemented, we still assign it to the slot as wrapped
- // native executable later on
- if (CApiContext.isIdenticalToSymbol(fieldPointer, NativeCAPISymbol.FUN_PYOBJECT_HASH_NOT_IMPLEMENTED)) {
- builder.set(def, TpSlotHashFun.HASH_NOT_IMPLEMENTED);
- continue;
- }
- } else if (def == TpSlotMeta.TP_ITERNEXT) {
- if (CApiContext.isIdenticalToSymbol(fieldPointer, NativeCAPISymbol.FUN_PY_OBJECT_NEXT_NOT_IMPLEMENTED)) {
- builder.set(def, TpSlotIterNext.NEXT_NOT_IMPLEMENTED);
- continue;
- }
- }
- } catch (UnsupportedMessageException e) {
- throw new IllegalStateException(e);
- }
- } else if (field instanceof TpSlotWrapper execWrapper) {
+ Object closureExecutable = ctx.getCApiContext().getClosureExecutable(fieldPtr);
+ if (closureExecutable instanceof TpSlotWrapper execWrapper) {
existingSlotWrapper = execWrapper;
+ } else if (closureExecutable != null) {
+ // This can happen for legacy slots where the delegate would be a PFunction
+ LOGGER.fine(() -> String.format("Unexpected executable for slot pointer: %s", closureExecutable));
+ } else if (def == TpSlotMeta.TP_HASH) {
+ // If the slot is not tp_iternext, but the value is
+ // PyObject_HashNotImplemented, we still assign it to the slot as wrapped
+ // native executable later on
+ if (CApiContext.isIdenticalToSymbol(fieldPtr, NativeCAPISymbol.FUN_PYOBJECT_HASH_NOT_IMPLEMENTED)) {
+ builder.set(def, TpSlotHashFun.HASH_NOT_IMPLEMENTED);
+ continue;
+ }
+ } else if (def == TpSlotMeta.TP_ITERNEXT) {
+ if (CApiContext.isIdenticalToSymbol(fieldPtr, NativeCAPISymbol.FUN_PY_OBJECT_NEXT_NOT_IMPLEMENTED)) {
+ builder.set(def, TpSlotIterNext.NEXT_NOT_IMPLEMENTED);
+ continue;
+ }
}
if (existingSlotWrapper != null) {
@@ -1334,9 +1317,9 @@ public static TpSlots fromNative(PythonAbstractNativeObject pythonClass, PythonC
// processed slot field, because user could have assigned some incompatible
// existing slot value into the slots field we're reading here
TpSlotWrapper newWrapper = existingSlotWrapper.cloneWith(newPythonSlot);
- toNative(pythonClass.getPtr(), def, newWrapper, ctx.getNativeNull());
+ toNative(pythonClass.getPtr(), def, newWrapper.getPointer());
// we need to continue with the new closure pointer
- field = def.readFromNative(pythonClass);
+ fieldPtr = def.readFromNative(pythonClass);
}
}
if (def.isValidSlotValue(newSlot)) {
@@ -1352,7 +1335,7 @@ public static TpSlots fromNative(PythonAbstractNativeObject pythonClass, PythonC
}
// There is no mapping from this pointer to existing TpSlot, we create a new
// TpSlotNative wrapping the executable
- Object executable = EnsureExecutableNode.executeUncached(field, def.nativeSignature);
+ NativeFunctionPointer executable = CExtCommonNodes.bindFunctionPointer(fieldPtr, def.nativeSignature);
builder.set(def, TpSlotNative.createCExtSlot(executable));
}
return builder.build();
@@ -1391,7 +1374,7 @@ public static void addOperatorsToNative(PythonAbstractNativeObject type) {
} else if (value instanceof TpSlotBuiltin> builtinSlot) {
wrapperDescriptor = builtinSlot.createBuiltin(context, type, tpSlotDef.name, tpSlotDef.wrapper);
} else if (value instanceof TpSlotNative nativeSlot) {
- wrapperDescriptor = PExternalFunctionWrapper.createWrapperFunction(tpSlotDef.name, nativeSlot.getCallable(), type, 0, tpSlotDef.wrapper, language);
+ wrapperDescriptor = PExternalFunctionWrapper.createDescrWrapperFunction(tpSlotDef.name, nativeSlot.getCallable(), type, tpSlotDef.wrapper, language);
} else if (value instanceof TpSlotPython) {
// There should already be a python method somewhere in the MRO or this doesn't
// make sense
@@ -1404,25 +1387,24 @@ public static void addOperatorsToNative(PythonAbstractNativeObject type) {
}
}
- public static void toNative(Object ptrToWrite, TpSlotMeta def, TpSlot value, Object nullValue) {
- assert !(ptrToWrite instanceof PythonAbstractNativeObject); // this should be the pointer
- Object slotNativeValue = def.getNativeValue(value, nullValue);
- toNative(ptrToWrite, def, slotNativeValue, nullValue);
+ public static void toNative(long ptrToWrite, TpSlotMeta def, TpSlot value) {
+ assert ptrToWrite != PythonAbstractObject.UNINITIALIZED; // this should be the pointer
+ long slotNativeValue = def.getNativeValue(value, NULLPTR);
+ toNative(ptrToWrite, def, slotNativeValue);
}
/**
* Writes back given managed slot to the native klass slots. This should be called any time we
* update the slots on the managed side to reflect that change in native.
*/
- private static void toNative(Object prtToWrite, TpSlotMeta def, Object slotNativeValue, Object nullValue) {
- assert !(slotNativeValue instanceof TpSlot); // this should be the native representation
- assert !(prtToWrite instanceof PythonAbstractNativeObject); // this should be the pointer
+ private static void toNative(long prtToWrite, TpSlotMeta def, long slotNativeValue) {
CompilerAsserts.neverPartOfCompilation();
CFields fieldToWrite = def.nativeGroupOrField;
+ long dest = prtToWrite;
if (def.nativeField != null) {
- prtToWrite = ReadPointerNode.getUncached().read(prtToWrite, def.nativeGroupOrField);
- if (InteropLibrary.getUncached().isNull(prtToWrite)) {
- if (slotNativeValue == nullValue) {
+ dest = readPtrField(prtToWrite, def.nativeGroupOrField);
+ if (prtToWrite == NULLPTR) {
+ if (slotNativeValue == NULLPTR) {
return;
} else {
throw new IllegalStateException("Trying to write a native slot whose group is not allocated. " +
@@ -1431,7 +1413,7 @@ private static void toNative(Object prtToWrite, TpSlotMeta def, Object slotNativ
}
fieldToWrite = def.nativeField;
}
- WritePointerNode.getUncached().write(prtToWrite, fieldToWrite, slotNativeValue);
+ writePtrField(dest, fieldToWrite, slotNativeValue);
}
@TruffleBoundary
@@ -1561,7 +1543,6 @@ private static void updateSlots(PythonAbstractClass klass, TpSlots slots, Set> slotdefGroups) {
LookupAttributeInMRONode.Dynamic lookup = LookupAttributeInMRONode.Dynamic.getUncached();
IsSubtypeNode isSubType = IsSubtypeNode.getUncached();
- Object nativeNull = PythonContext.get(null).getNativeNull();
for (var slotdefGroup : slotdefGroups) {
TpSlotMeta slot = slotdefGroup.getKey(); // ~ "ptr" in CPython algorithm
if (slot.hasGroup() && !slots.hasGroup(slot.getGroup())) {
@@ -1687,17 +1668,13 @@ private static Builder updateSlots(PythonAbstractClass klass, Builder slots, Set
slots.set(slot, newValue);
if (klass instanceof PythonAbstractNativeObject nativeClass) {
// Update the slots on the native side if this is a native class
- toNative(nativeClass.getPtr(), slot, newValue, nativeNull);
+ toNative(nativeClass.getPtr(), slot, newValue);
}
if (klass instanceof PythonManagedClass managedClass) {
// Update the slots on the native side if this is a managed class that has a
// native mirror allocated already
- PythonClassNativeWrapper classNativeWrapper = managedClass.getClassNativeWrapper();
- if (classNativeWrapper != null) {
- Object replacement = classNativeWrapper.getReplacementIfInitialized();
- if (replacement != null) {
- toNative(replacement, slot, newValue, nativeNull);
- }
+ if (managedClass.isNative()) {
+ toNative(managedClass.getNativePointer(), slot, newValue);
}
}
}
@@ -1705,7 +1682,7 @@ private static Builder updateSlots(PythonAbstractClass klass, Builder slots, Set
}
private static boolean areSameNativeCallables(TpSlot a, TpSlot b) {
- return a instanceof TpSlotNative na && b instanceof TpSlotNative nb && na.isSameCallable(nb, InteropLibrary.getUncached());
+ return a instanceof TpSlotNative na && b instanceof TpSlotNative nb && na.isSameCallable(nb);
}
public static void setSlots(PythonAbstractClass klass, TpSlots slots) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java
index 4d0c34e6e8..087e7536a6 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java
@@ -30,6 +30,7 @@
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyHeapTypeObject__ht_name;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyHeapTypeObject__ht_qualname;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_name;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writePtrField;
import static com.oracle.graal.python.nodes.BuiltinNames.T_BUILTINS;
import static com.oracle.graal.python.nodes.ErrorMessages.ATTR_NAME_MUST_BE_STRING;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___ABSTRACTMETHODS__;
@@ -894,7 +895,6 @@ static void set(PythonClass type, TruffleString value) {
@Specialization
static void set(Node inliningTarget, PythonAbstractNativeObject type, TruffleString value,
@Bind PythonLanguage language,
- @Cached(inline = false) CStructAccess.WritePointerNode writePointerNode,
@Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObject,
@Cached HiddenAttr.WriteNode writeAttrNode,
@Cached TruffleString.SwitchEncodingNode switchEncodingNode,
@@ -902,7 +902,8 @@ static void set(Node inliningTarget, PythonAbstractNativeObject type, TruffleStr
value = switchEncodingNode.execute(value, TruffleString.Encoding.UTF_8);
byte[] bytes = copyToByteArrayNode.execute(value, TruffleString.Encoding.UTF_8);
PBytes utf8Bytes = PFactory.createBytes(language, bytes);
- writePointerNode.writeToObj(type, PyTypeObject__tp_name, PySequenceArrayWrapper.ensureNativeSequence(utf8Bytes));
+ long typeRawPtr = type.getPtr();
+ writePtrField(typeRawPtr, PyTypeObject__tp_name, PySequenceArrayWrapper.ensureNativeSequence(utf8Bytes));
PString pString = PFactory.createString(language, value);
writeAttrNode.execute(inliningTarget, pString, HiddenAttr.PSTRING_UTF8, utf8Bytes);
writeObject.writeToObject(type, PyHeapTypeObject__ht_name, pString);
@@ -982,6 +983,7 @@ static Object getModule(PythonAbstractNativeObject cls, @SuppressWarnings("unuse
@Cached TruffleString.SubstringNode substringNode,
@Shared @Cached PRaiseNode raiseNode) {
// see function 'typeobject.c: type_module'
+ assert IsTypeNode.executeUncached(cls);
if ((getFlags.execute(cls) & TypeFlags.HEAPTYPE) != 0) {
Object module = readAttrNode.execute(cls, T___MODULE__);
if (module == NO_VALUE) {
@@ -1066,6 +1068,7 @@ static void set(PythonClass type, TruffleString value) {
@Specialization
static void set(PythonAbstractNativeObject type, TruffleString value,
@Cached(inline = false) CStructAccess.WriteObjectNewRefNode writeObject) {
+ assert IsTypeNode.executeUncached(type);
writeObject.writeToObject(type, PyHeapTypeObject__ht_qualname, value);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java
index 5d93adacb8..050f9a87e0 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java
@@ -54,6 +54,8 @@
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_name;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_subclasses;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_weaklistoffset;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeLongField;
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.BASETYPE;
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.BASE_EXC_SUBCLASS;
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.BYTES_SUBCLASS;
@@ -107,16 +109,19 @@
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.modules.WeakRefModuleBuiltins.GetWeakRefsNode;
import com.oracle.graal.python.builtins.modules.WeakRefModuleBuiltinsFactory;
-import com.oracle.graal.python.builtins.modules.cext.PythonCextTypeBuiltins.GraalPyPrivate_Type_AddMember;
+import com.oracle.graal.python.builtins.modules.cext.PythonCextTypeBuiltins;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cell.PCell;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiMemberAccessNodes;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitionsFactory.PythonToNativeNodeGen;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
@@ -203,6 +208,7 @@
import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode;
import com.oracle.graal.python.nodes.object.GetOrCreateDictNode;
+import com.oracle.graal.python.nodes.object.IsNode;
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
import com.oracle.graal.python.nodes.util.LazyInteropLibrary;
@@ -239,7 +245,6 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.ControlFlowException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.Shape;
@@ -294,9 +299,8 @@ static long doManaged(PythonManagedClass clazz,
@Specialization
@InliningCutoff
- static long doNative(PythonNativeClass clazz,
- @Cached CStructAccess.ReadI64Node getTpFlagsNode) {
- return getTpFlagsNode.readFromObj(clazz, PyTypeObject__tp_flags);
+ static long doNative(PythonNativeClass clazz) {
+ return readLongField(clazz.getPtr(), PyTypeObject__tp_flags);
}
@TruffleBoundary
@@ -327,7 +331,7 @@ private static long computeFlags(PythonManagedClass clazz) {
mroEntry = context.getCore().lookupType((PythonBuiltinClassType) mroEntry);
}
if (mroEntry instanceof PythonAbstractNativeObject) {
- result = setFlags(result, doNative((PythonAbstractNativeObject) mroEntry, CStructAccess.ReadI64Node.getUncached()));
+ result = setFlags(result, doNative((PythonAbstractNativeObject) mroEntry));
} else if (mroEntry != clazz && mroEntry instanceof PythonManagedClass) {
long flags = doManaged((PythonManagedClass) mroEntry, null, HiddenAttr.ReadNode.getUncached(), HiddenAttr.WriteNode.getUncached(),
InlinedCountingConditionProfile.getUncached());
@@ -506,9 +510,8 @@ static void doManaged(Node inliningTarget, PythonManagedClass clazz, long flags,
}
@Specialization
- static void doNative(PythonNativeClass clazz, long flags,
- @Cached(inline = false) CStructAccess.WriteLongNode write) {
- write.writeToObject(clazz, PyTypeObject__tp_flags, flags);
+ static void doNative(PythonNativeClass clazz, long flags) {
+ writeLongField(clazz.getPtr(), PyTypeObject__tp_flags, flags);
}
}
@@ -556,6 +559,7 @@ public static GetMroNode create() {
@GenerateInline(inlineByDefault = true)
@GenerateCached
public abstract static class GetMroStorageNode extends PNodeWithContext {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_PY_TYPE_READY);
public abstract MroSequenceStorage execute(Node inliningTarget, Object obj);
@@ -627,7 +631,11 @@ private static Object initializeType(Node inliningTarget, PythonNativeClass obj,
CompilerDirectives.transferToInterpreter();
// call 'PyType_Ready' on the type
- int res = (int) PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_PY_TYPE_READY, PythonToNativeNodeGen.getUncached().execute(obj));
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(obj);
+ PythonContext context = PythonContext.get(null);
+ var callable = CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_PY_TYPE_READY);
+ int res = ExternalFunctionInvoker.invokePY_TYPE_READY(null, C_API_TIMING, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(), context.getThreadState(PythonLanguage.get(inliningTarget)), callable, PythonToNativeNode.executeLongUncached(obj));
if (res < 0) {
throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.LAZY_INITIALIZATION_FAILED, obj);
}
@@ -702,6 +710,7 @@ TruffleString doNativeClass(PythonNativeClass obj,
@Cached TruffleString.CodePointLengthNode codePointLengthNode,
@Cached TruffleString.LastIndexOfCodePointNode indexOfCodePointNode,
@Cached TruffleString.SubstringNode substringNode) {
+ assert IsTypeNode.executeUncached(obj);
// 'tp_name' contains the fully-qualified name, i.e., 'module.A.B...'
TruffleString tpName = getTpNameNode.readFromObj(obj, PyTypeObject__tp_name);
int nameLen = codePointLengthNode.execute(tpName, TS_ENCODING);
@@ -746,6 +755,7 @@ static TruffleString doBuiltinClassType(PythonBuiltinClassType obj) {
@Specialization
TruffleString doNativeClass(PythonNativeClass obj,
@Cached(inline = false) CStructAccess.ReadCharPtrNode getTpNameNode) {
+ assert IsTypeNode.executeUncached(obj);
return getTpNameNode.readFromObj(obj, PyTypeObject__tp_name);
}
}
@@ -1275,9 +1285,8 @@ static boolean doPythonClass(PythonManagedClass type) {
}
@Specialization
- static boolean doNativeObject(PythonAbstractNativeObject type,
- @Cached CStructAccess.ReadI64Node getMember) {
- return getMember.readFromObj(type, PyTypeObject__tp_dictoffset) != 0;
+ static boolean doNativeObject(PythonAbstractNativeObject type) {
+ return readLongField(type.getPtr(), PyTypeObject__tp_dictoffset) != 0;
}
@Fallback
@@ -1499,16 +1508,8 @@ static boolean doClassType(PythonBuiltinClass left, PythonBuiltinClassType right
}
@Specialization
- @InliningCutoff
- static boolean doNative(PythonAbstractNativeObject left, PythonAbstractNativeObject right,
- @CachedLibrary(limit = "1") InteropLibrary lib) {
- if (left == right) {
- return true;
- }
- if (left.getPtr() instanceof Long && right.getPtr() instanceof Long) {
- return (long) left.getPtr() == (long) right.getPtr();
- }
- return lib.isIdentical(left.getPtr(), right.getPtr(), lib);
+ static boolean doNative(PythonAbstractNativeObject left, PythonAbstractNativeObject right) {
+ return left == right || left.getPtr() == right.getPtr();
}
@Fallback
@@ -1577,18 +1578,28 @@ static PythonBuiltinClassType doPythonBuiltinClassType(@SuppressWarnings("unused
return cachedClassType;
}
- @Specialization(guards = {"isSingleContext()", "isPythonAbstractClass(object)"}, rewriteOn = NotSameTypeException.class)
- static Object doPythonAbstractClass(Object object,
- @Cached(value = "object", weak = true) Object cachedObject,
- @CachedLibrary(limit = "2") InteropLibrary lib) throws NotSameTypeException {
- if (lib.isIdentical(object, cachedObject, lib)) {
+ @Specialization(guards = {"isSingleContext()"}, rewriteOn = NotSameTypeException.class)
+ static Object doPythonNativeClass(PythonAbstractNativeObject object,
+ @Cached(value = "object", weak = true) PythonAbstractNativeObject cachedObject) throws NotSameTypeException {
+ if (object.getPtr() == cachedObject.getPtr()) {
+ return cachedObject;
+ }
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ throw NotSameTypeException.INSTANCE;
+ }
+
+ @Specialization(guards = {"isSingleContext()"}, rewriteOn = NotSameTypeException.class)
+ static Object doPythonManagedClass(PythonManagedClass object,
+ @Cached(value = "object", weak = true) PythonManagedClass cachedObject,
+ @Cached IsNode isNode) throws NotSameTypeException {
+ if (object == cachedObject || isNode.execute(object, cachedObject)) {
return cachedObject;
}
CompilerDirectives.transferToInterpreterAndInvalidate();
throw NotSameTypeException.INSTANCE;
}
- @Specialization(replaces = {"doPythonBuiltinClassType", "doPythonAbstractClass"})
+ @Specialization(replaces = {"doPythonBuiltinClassType", "doPythonNativeClass", "doPythonManagedClass"})
static Object doDisabled(Object object) {
return object;
}
@@ -1772,8 +1783,7 @@ static boolean doBuiltinType(@SuppressWarnings("unused") PythonBuiltinClassType
@Specialization
static boolean doNativeClass(Node inliningTarget, PythonAbstractNativeObject obj,
@Cached IsBuiltinClassProfile profile,
- @Cached GetPythonObjectClassNode getClassNode,
- @Cached CStructAccess.ReadI64Node getTpFlagsNode) {
+ @Cached GetPythonObjectClassNode getClassNode) {
Object type = getClassNode.execute(inliningTarget, obj);
if (profile.profileClass(inliningTarget, type, PythonBuiltinClassType.PythonClass)) {
return true;
@@ -1781,7 +1791,7 @@ static boolean doNativeClass(Node inliningTarget, PythonAbstractNativeObject obj
if (PythonNativeClass.isInstance(type)) {
// Equivalent of PyType_FastSubclass(Py_TYPE(type), Py_TPFLAGS_TYPE_SUBCLASS);
- long tp_flags = getTpFlagsNode.readFromObj(PythonNativeClass.cast(type), PyTypeObject__tp_flags);
+ long tp_flags = readLongField(PythonNativeClass.cast(type).getPtr(), PyTypeObject__tp_flags);
return (tp_flags & TYPE_SUBCLASS) != 0;
}
return false;
@@ -2412,7 +2422,7 @@ private static void addNativeSlots(TypeNewContext ctx, PythonManagedClass python
private static long installMemberDescriptors(PythonManagedClass pythonClass, TruffleString[] slotNames, long slotOffset) {
PDict typeDict = GetOrCreateDictNode.executeUncached(pythonClass);
for (TruffleString slotName : slotNames) {
- GraalPyPrivate_Type_AddMember.addMember(pythonClass, typeDict, slotName, CApiMemberAccessNodes.T_OBJECT_EX, slotOffset, 1, PNone.NO_VALUE);
+ PythonCextTypeBuiltins.addMember(pythonClass, typeDict, slotName, CApiMemberAccessNodes.T_OBJECT_EX, slotOffset, 1, PNone.NO_VALUE);
slotOffset += SIZEOF_PY_OBJECT_PTR;
}
return slotOffset;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlot.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlot.java
index 0151dfd343..1474f74336 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlot.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlot.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -47,16 +47,16 @@
import java.util.concurrent.atomic.AtomicInteger;
import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.annotations.Builtin;
import com.oracle.graal.python.annotations.Slot;
import com.oracle.graal.python.annotations.Slot.SlotSignature;
-import com.oracle.graal.python.annotations.Builtin;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.TpSlotWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.object.PythonObject;
import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotMeta;
@@ -65,6 +65,7 @@
import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode;
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.RootCallTarget;
@@ -74,9 +75,6 @@
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.utilities.TruffleWeakReference;
@@ -90,14 +88,14 @@ public abstract class TpSlot {
/**
* Transforms the slot object to an interop object that can be sent to native.
*/
- public static Object toNative(TpSlotMeta slotMeta, TpSlot slot, Object defaultValue) {
+ public static long toNative(TpSlotMeta slotMeta, TpSlot slot, long defaultValue) {
if (slot == null) {
return defaultValue;
} else if (slot instanceof TpSlotNative nativeSlot) {
- return nativeSlot.getCallable();
+ return nativeSlot.getCallable().getAddress();
} else if (slot instanceof TpSlotManaged managedSlot) {
- // This returns PyProcsWrapper, which will, in its toNative message, register the
- // pointer in C API context, such that we can map back from a pointer that we get from C
+ // This constructs PyProcsWrapper, which will register its pointer in C API context,
+ // such that we can map back from a pointer that we get from C
// to the PyProcsWrapper and from that to the slot instance again in TpSlots#fromNative
return getNativeWrapper(slotMeta, managedSlot);
} else {
@@ -105,21 +103,21 @@ public static Object toNative(TpSlotMeta slotMeta, TpSlot slot, Object defaultVa
}
}
- private static Object getNativeWrapper(TpSlotMeta slotMeta, TpSlotManaged slot) {
+ private static long getNativeWrapper(TpSlotMeta slotMeta, TpSlotManaged slot) {
if (slot == TpSlotHashFun.HASH_NOT_IMPLEMENTED) {
// If there are more such cases, we should add generic mapping mechanism
// This translation other way around is also done in TpSlots.fromNative
// We must not cache this in the singleton slot object, it would hold onto and leak
// Python objects
- return CApiContext.getNativeSymbol(null, FUN_PYOBJECT_HASH_NOT_IMPLEMENTED);
+ return CApiContext.getNativeSymbol(null, FUN_PYOBJECT_HASH_NOT_IMPLEMENTED).getAddress();
} else if (slot == TpSlotIterNext.NEXT_NOT_IMPLEMENTED) {
- return CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_PY_OBJECT_NEXT_NOT_IMPLEMENTED);
+ return CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_PY_OBJECT_NEXT_NOT_IMPLEMENTED).getAddress();
}
assert PythonContext.get(null).ownsGil(); // without GIL: use AtomicReference & CAS
if (slot.slotWrapper == null) {
slot.slotWrapper = slotMeta.createNativeWrapper(slot);
}
- return slot.slotWrapper;
+ return slot.slotWrapper.getPointer();
}
/**
@@ -139,9 +137,8 @@ static boolean pythonWrappers(TpSlotPython a, TpSlotPython b) {
}
@Specialization
- static boolean nativeSlots(TpSlotNative a, TpSlotNative b,
- @CachedLibrary(limit = "1") InteropLibrary interop) {
- return a.isSameCallable(b, interop);
+ static boolean nativeSlots(TpSlotNative a, TpSlotNative b) {
+ return a.isSameCallable(b);
}
@Fallback
@@ -164,7 +161,7 @@ public abstract static sealed class TpSlotManaged extends TpSlot permits TpSlotB
* Represents native callable that delegates to this slot. Should be {@link TpSlotWrapper}
* most of the time, but we allow overriding those wrappers with native implementation.
*/
- private Object slotWrapper;
+ private TpSlotWrapper slotWrapper;
}
/**
@@ -206,36 +203,24 @@ static TruffleWeakReference asWeakRef(Object value) {
* array and cannot optimize for specific signature.
*/
public abstract static sealed class TpSlotNative extends TpSlot permits TpSlotCExtNative {
- final Object callable;
+ final NativeFunctionPointer callable;
- public TpSlotNative(Object callable) {
+ public TpSlotNative(NativeFunctionPointer callable) {
this.callable = callable;
}
- public static TpSlotNative createCExtSlot(Object callable) {
+ public static TpSlotNative createCExtSlot(NativeFunctionPointer callable) {
return new TpSlotCExtNative(callable);
}
- public final boolean isSameCallable(TpSlotNative other, InteropLibrary interop) {
- if (this == other || this.callable == other.callable) {
- return true;
- }
- // NFISymbols do not implement isIdentical interop message, so we compare the pointers
- // Interop is going to be quite slow (in interpreter), should we eagerly request the
- // pointer in the ctor?
- interop.toNative(callable);
- interop.toNative(other.callable);
- try {
- return interop.asPointer(callable) == interop.asPointer(other.callable);
- } catch (UnsupportedMessageException e) {
- throw CompilerDirectives.shouldNotReachHere(e);
- }
+ public final boolean isSameCallable(TpSlotNative other) {
+ return this == other || this.callable == other.callable || callable.getAddress() == other.callable.getAddress();
}
/**
- * Bound callable that supports the execute interop message.
+ * The native function that implements the slot.
*/
- public final Object getCallable() {
+ public final NativeFunctionPointer getCallable() {
return callable;
}
}
@@ -244,7 +229,7 @@ public final Object getCallable() {
* Standard CPython C API slot that takes {@code PyObject*} arguments.
*/
public static final class TpSlotCExtNative extends TpSlotNative {
- public TpSlotCExtNative(Object callable) {
+ public TpSlotCExtNative(NativeFunctionPointer callable) {
super(callable);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotBinaryFunc.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotBinaryFunc.java
index c6d9f28dc7..379ebab99c 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotBinaryFunc.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotBinaryFunc.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -44,23 +44,29 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___GETITEM__;
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.type.slots.PythonDispatchers.BinaryPythonSlotDispatcherNode;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotBuiltinBase;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFuncFactory.CallSlotBinaryFuncNodeGen;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Cached;
@@ -127,6 +133,11 @@ public abstract static class CallSlotBinaryFuncNode extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self, Object arg);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self, Object arg) {
+ return CallSlotBinaryFuncNodeGen.getUncached().execute(null, null, slot, self, arg);
+ }
+
@Specialization(guards = "cachedSlot == slot", limit = "3")
static Object callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotBinaryFuncBuiltin> slot, Object self, Object arg,
@SuppressWarnings("unused") @Cached("slot") TpSlotBinaryFuncBuiltin> cachedSlot,
@@ -143,16 +154,24 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi
@Specialization
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self, Object arg,
@Exclusive @Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode selfToNativeNode,
@Cached(inline = false) PythonToNativeNode argToNativeNode,
- @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Exclusive @Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T_BINARY_SLOT, slot.callable,
- selfToNativeNode.execute(self), argToNativeNode.execute(arg));
- return checkResultNode.execute(state, T_BINARY_SLOT, toPythonNode.execute(result));
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ Object promotedArg = ensurePythonObjectNode.execute(ctx, arg, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeBINARYFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ selfToNativeNode.executeLong(promotedSelf), argToNativeNode.executeLong(promotedArg));
+ return checkResultNode.execute(state, T_BINARY_SLOT, toPythonNode.execute(inliningTarget, lresult, true));
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(promotedArg);
+ }
}
@Specialization(replaces = "callCachedBuiltin")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotBinaryOp.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotBinaryOp.java
index 52e1205bbc..5bfa9e2226 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotBinaryOp.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotBinaryOp.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -69,16 +69,18 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___TRUEDIV__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___XOR__;
+import java.lang.ref.Reference;
import java.util.Arrays;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.objects.PNotImplemented;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
@@ -92,6 +94,7 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPython;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.TpSlotBinaryFuncBuiltin;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOpFactory.CallSlotBinaryOpNodeGen;
import com.oracle.graal.python.lib.PyObjectRichCompareBool;
import com.oracle.graal.python.lib.RichCmpOp;
import com.oracle.graal.python.nodes.PGuards;
@@ -99,9 +102,11 @@
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -345,6 +350,11 @@ public abstract static class CallSlotBinaryOpNode extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self, Object selfType, Object other, TpSlot otherSlot, Object otherType, boolean sameTypes,
ReversibleSlot op);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self, Object selfType, Object other, TpSlot otherSlot, Object otherType, boolean sameTypes, ReversibleSlot op) {
+ return CallSlotBinaryOpNodeGen.getUncached().execute(null, null, slot, self, selfType, other, otherSlot, otherType, sameTypes, op);
+ }
+
@SuppressWarnings("unused")
@Specialization(guards = "cachedSlot == slot", limit = "3")
static Object callCachedBuiltin(VirtualFrame frame, TpSlotBinaryOpBuiltin> slot, Object self,
@@ -365,16 +375,24 @@ static Object callPython(VirtualFrame frame, TpSlotReversiblePython slot, Object
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self,
Object selfType, Object arg, TpSlot otherSlot, Object otherType, boolean sameTypes, ReversibleSlot op,
@Exclusive @Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode selfToNativeNode,
@Cached(inline = false) PythonToNativeNode argToNativeNode,
- @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Exclusive @Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, op.name, slot.callable,
- selfToNativeNode.execute(self), argToNativeNode.execute(arg));
- return checkResultNode.execute(state, op.name, toPythonNode.execute(result));
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ Object promotedArg = ensurePythonObjectNode.execute(ctx, arg, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeBINARYFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ selfToNativeNode.executeLong(promotedSelf), argToNativeNode.executeLong(promotedArg));
+ return checkResultNode.execute(state, op.name, toPythonNode.execute(inliningTarget, lresult, true));
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(promotedArg);
+ }
}
@SuppressWarnings("unused")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrGet.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrGet.java
index 2316f2e283..14495445b8 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrGet.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrGet.java
@@ -44,15 +44,18 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___GET__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GET__;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
@@ -64,14 +67,15 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGetFactory.CallSlotDescrGetNodeGen;
import com.oracle.graal.python.nodes.ErrorMessages;
-import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.call.CallNode;
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
+import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
@@ -160,14 +164,10 @@ static final class DescrGetWrapperNode extends PythonTernaryBuiltinNode {
this.wrapped = wrapped;
}
- private static Object normalizeNone(ConditionProfile profile, Object o) {
- return profile.profile(PGuards.isNone(o)) ? PNone.NO_VALUE : o;
- }
-
@Override
public Object execute(VirtualFrame frame, Object self, Object objIn, Object typeIn) {
- Object obj = normalizeNone(objIsNoneProfile, objIn);
- Object type = normalizeNone(typeIsNoneProfile, typeIn);
+ Object obj = PythonUtils.normalizeNone(objIsNoneProfile, objIn);
+ Object type = PythonUtils.normalizeNone(typeIsNoneProfile, typeIn);
if (obj == PNone.NO_VALUE && type == PNone.NO_VALUE) {
errorProfile.enter();
throw PRaiseNode.raiseStatic(this, PythonBuiltinClassType.TypeError, ErrorMessages.GET_NONE_NONE_IS_INVALID);
@@ -197,6 +197,11 @@ public final Object executeCached(VirtualFrame frame, TpSlot slot, Object self,
return execute(frame, this, slot, self, obj, type);
}
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self, Object obj, Object type) {
+ return CallSlotDescrGetNodeGen.getUncached().execute(null, null, slot, self, obj, type);
+ }
+
@NeverDefault
public static CallSlotDescrGet create() {
return CallSlotDescrGetNodeGen.create();
@@ -219,19 +224,29 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi
@Specialization
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotNative slot, Object self, Object obj, Object value,
@Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode selfToNativeNode,
@Cached(inline = false) PythonToNativeNode objToNativeNode,
@Cached(inline = false) PythonToNativeNode valueToNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, T___GET__, slot.callable, //
- selfToNativeNode.execute(self), //
- objToNativeNode.execute(obj), //
- valueToNativeNode.execute(value));
- return checkResultNode.execute(threadState, T___GET__, toPythonNode.execute(result));
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ Object promotedObj = ensurePythonObjectNode.execute(ctx, obj, false);
+ Object promotedValue = ensurePythonObjectNode.execute(ctx, value, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeDESCRGETFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, threadState, slot.callable,
+ selfToNativeNode.executeLong(promotedSelf), //
+ objToNativeNode.executeLong(promotedObj), //
+ valueToNativeNode.executeLong(promotedValue));
+ return checkResultNode.execute(threadState, T___GET__, toPythonNode.execute(inliningTarget, lresult, true));
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(promotedObj);
+ Reference.reachabilityFence(promotedValue);
+ }
}
@TruffleBoundary
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrSet.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrSet.java
index 015f411c02..f3d0fc8af9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrSet.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrSet.java
@@ -46,15 +46,17 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___DEL__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SET__;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.InitCheckFunctionResultNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
@@ -65,15 +67,18 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPython;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSetFactory.CallSlotDescrSetNodeGen;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargs.InitCheckFunctionResultNode;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode.Dynamic;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Cached;
@@ -193,18 +198,28 @@ abstract static class CallNativeSlotDescrSet extends Node {
@Specialization
static void callNative(VirtualFrame frame, Node inliningTarget, TpSlotNative slot, Object self, Object obj, Object value,
@Cached GetThreadStateNode getThreadStateNode,
- @Cached(inline = false) PythonToNativeNode selfToNativeNode,
- @Cached(inline = false) PythonToNativeNode objToNativeNode,
- @Cached(inline = false) PythonToNativeNode valueToNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) InitCheckFunctionResultNode checkResultNode) {
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached PythonToNativeInternalNode selfToNativeNode,
+ @Cached PythonToNativeInternalNode objToNativeNode,
+ @Cached PythonToNativeInternalNode valueToNativeNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached InitCheckFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
- PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, T___SET__, slot.callable, //
- selfToNativeNode.execute(self), //
- objToNativeNode.execute(obj), //
- valueToNativeNode.execute(value));
- checkResultNode.execute(threadState, T___SET__, result);
+ PythonThreadState threadState = getThreadStateNode.execute(inliningTarget);
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ Object promotedObj = ensurePythonObjectNode.execute(ctx, obj, false);
+ Object promotedValue = ensurePythonObjectNode.execute(ctx, value, false);
+ try {
+ int iresult = ExternalFunctionInvoker.invokeDESCRSETFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, threadState, slot.callable,
+ selfToNativeNode.execute(inliningTarget, promotedSelf, false), //
+ objToNativeNode.execute(inliningTarget, promotedObj, false), //
+ valueToNativeNode.execute(inliningTarget, promotedValue, false));
+ checkResultNode.executeInt(inliningTarget, threadState, T___SET__, iresult);
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(promotedObj);
+ Reference.reachabilityFence(promotedValue);
+ }
}
}
@@ -226,6 +241,11 @@ public static CallSlotDescrSet create() {
public abstract void execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self, Object obj, Object value);
+ @TruffleBoundary
+ public static void executeUncached(TpSlot slot, Object self, Object obj, Object value) {
+ CallSlotDescrSetNodeGen.getUncached().execute(null, null, slot, self, obj, value);
+ }
+
public final void executeCached(VirtualFrame frame, TpSlot slot, Object self, Object obj, Object value) {
execute(frame, this, slot, self, obj, value);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotGetAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotGetAttr.java
index 5f788bff56..048b9d0087 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotGetAttr.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotGetAttr.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -40,22 +40,25 @@
*/
package com.oracle.graal.python.builtins.objects.type.slots;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.free;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___GETATTRIBUTE__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETATTRIBUTE__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETATTR__;
import static com.oracle.graal.python.runtime.exception.PythonErrorType.AttributeError;
import java.lang.ref.Reference;
+import java.util.logging.Level;
import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.AsCharPointerNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.FreeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
import com.oracle.graal.python.builtins.objects.type.slots.PythonDispatchers.BinaryPythonSlotDispatcherNode;
@@ -63,6 +66,7 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotManaged;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPython;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttrFactory.CallManagedSlotGetAttrNodeGen;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode.Dynamic;
import com.oracle.graal.python.nodes.call.CallDispatchers;
@@ -70,11 +74,16 @@
import com.oracle.graal.python.nodes.call.special.MaybeBindDescriptorNode;
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
@@ -235,6 +244,7 @@ static Object callNative(VirtualFrame frame, TpSlots slots, TpSlotNative slot, O
@GenerateInline(false) // Used lazily
@GenerateUncached
abstract static class CallNativeSlotGetAttrNode extends Node {
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(CallNativeSlotGetAttrNode.class);
private static final CApiTiming C_API_TIMING = CApiTiming.create(true, "tp_getattr");
abstract Object execute(VirtualFrame frame, TpSlots slots, TpSlotNative tp_get_attro_attr, Object self, Object name);
@@ -242,34 +252,43 @@ abstract static class CallNativeSlotGetAttrNode extends Node {
@Specialization
static Object callNative(VirtualFrame frame, TpSlots slots, TpSlotNative slot, Object self, Object name,
@Bind Node inliningTarget,
+ @Bind PythonContext context,
@Cached GetThreadStateNode getThreadStateNode,
@Cached InlinedConditionProfile isGetAttrProfile,
@Cached AsCharPointerNode asCharPointerNode,
- @Cached FreeNode freeNode,
@Cached PythonToNativeNode nameToNativeNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached PythonToNativeNode selfToNativeNode,
@Cached NativeToPythonTransferNode toPythonNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
@Cached PyObjectCheckFunctionResultNode checkResultNode) {
boolean isGetAttr = isGetAttrProfile.profile(inliningTarget, slots.tp_getattr() == slot);
- Object nameArg;
+ Object promotedSelf = ensurePythonObjectNode.execute(context, self, false);
+ Object promotedName = null;
+ long nameArg;
if (isGetAttr) {
nameArg = asCharPointerNode.execute(name);
} else {
- nameArg = nameToNativeNode.execute(name);
+ promotedName = ensurePythonObjectNode.execute(context, name, false);
+ nameArg = nameToNativeNode.executeLong(promotedName);
}
- Object result;
- PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, null);
+ long lresult;
+ PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, context);
try {
- result = externalInvokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, T___GETATTR__, slot.callable, selfToNativeNode.execute(self), nameArg);
+ lresult = ExternalFunctionInvoker.invokeGETATTRFUNC(frame, C_API_TIMING, context.ensureNativeContext(), boundaryCallData, threadState, slot.callable,
+ selfToNativeNode.executeLong(promotedSelf), nameArg);
} finally {
+ Reference.reachabilityFence(promotedSelf);
if (isGetAttr) {
- freeNode.free(nameArg);
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Freeing name (const char *)0x%x", nameArg));
+ }
+ free(nameArg);
} else {
- Reference.reachabilityFence(nameArg);
+ Reference.reachabilityFence(promotedName);
}
}
- return checkResultNode.execute(threadState, T___GETATTR__, toPythonNode.execute(result));
+ return checkResultNode.execute(threadState, T___GETATTR__, toPythonNode.executeRaw(lresult));
}
}
@@ -283,6 +302,11 @@ public abstract static class CallManagedSlotGetAttrNode extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlotManaged slot, Object self, TruffleString name);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlotManaged slot, Object self, Object name) {
+ return CallManagedSlotGetAttrNodeGen.getUncached().execute(null, null, slot, self, name);
+ }
+
@Specialization(guards = "slot == cachedSlot", limit = "3")
static Object callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotGetAttrBuiltin slot, Object self, TruffleString name,
@SuppressWarnings("unused") @Cached("slot") TpSlotGetAttrBuiltin cachedSlot,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotHashFun.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotHashFun.java
index 47e0c25072..b6aac455d3 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotHashFun.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotHashFun.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -43,11 +43,14 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___HASH__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___HASH__;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckPrimitiveFunctionResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckPrimitiveFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
@@ -67,11 +70,13 @@
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.util.OverflowException;
import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -96,7 +101,7 @@ private TpSlotHashFun() {
/**
* Mirror of the {@code PyObject_HashNotImplemented} slot on the managed side. We translate this
* slot singleton instance to a pointer to the {@code PyObject_HashNotImplemented} C function in
- * {@link TpSlot#toNative(TpSlotMeta, TpSlot, Object)} and vice versa in
+ * {@link TpSlot#toNative(TpSlotMeta, TpSlot, long)} and vice versa in
* {@code TpSlot#fromNative(PythonContext, Object, InteropLibrary)}.
*/
public static class PyObjectHashNotImplemented extends TpSlotHashBuiltin {
@@ -152,6 +157,11 @@ public abstract static class CallSlotHashFunNode extends Node {
public abstract long execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self);
+ @TruffleBoundary
+ public static long executeUncached(TpSlot slot, Object self) {
+ return CallSlotHashFunNodeGen.getUncached().execute(null, null, slot, self);
+ }
+
@Specialization(guards = "cachedSlot == slot", limit = "3")
static long callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotHashBuiltin> slot, Object self,
@SuppressWarnings("unused") @Cached("slot") TpSlotHashBuiltin> cachedSlot,
@@ -169,12 +179,19 @@ static long callPython(VirtualFrame frame, TpSlotPythonSingle slot, Object self,
static long callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self,
@Exclusive @Cached GetThreadStateNode getThreadStateNode,
@Cached(inline = false) PythonToNativeNode toNativeNode,
- @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Exclusive @Cached(inline = false) CheckPrimitiveFunctionResultNode checkResultNode) {
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Exclusive @Cached CheckPrimitiveFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T___HASH__, slot.callable, toNativeNode.execute(self));
- return checkResultNode.executeLong(state, T___HASH__, result);
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeHASHFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ toNativeNode.executeLong(promotedSelf));
+ return checkResultNode.executeLong(inliningTarget, state, T___HASH__, lresult);
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ }
}
@Specialization(replaces = "callCachedBuiltin")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotInquiry.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotInquiry.java
index 10c6272548..3268aaa9c1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotInquiry.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotInquiry.java
@@ -44,26 +44,32 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___BOOL__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___BOOL__;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckInquiryResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionFromNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.type.slots.PythonDispatchers.UnaryPythonSlotDispatcherNode;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotBuiltinBase;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiryFactory.CallSlotNbBoolNodeGen;
import com.oracle.graal.python.lib.PyBoolCheckNode;
import com.oracle.graal.python.lib.PyObjectIsTrueNode;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -75,6 +81,8 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.InlinedConditionProfile;
+import com.oracle.truffle.api.strings.TruffleString;
public abstract class TpSlotInquiry {
private TpSlotInquiry() {
@@ -85,7 +93,7 @@ public abstract static sealed class TpSlotInquiryBuiltin nodeFactory) {
- super(nodeFactory, SIGNATURE, PExternalFunctionWrapper.INQUIRY);
+ super(nodeFactory, SIGNATURE, PExternalFunctionWrapper.INQUIRYPRED);
}
final InquiryBuiltinNode createSlotNode() {
@@ -143,6 +151,11 @@ public abstract static class CallSlotNbBoolNode extends Node {
public abstract boolean execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self);
+ @TruffleBoundary
+ public static boolean executeUncached(TpSlot slot, Object self) {
+ return CallSlotNbBoolNodeGen.getUncached().execute(null, null, slot, self);
+ }
+
@Specialization(guards = "slot == cachedSlot", limit = "3")
static boolean callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotInquiryBuiltin slot, Object self,
@SuppressWarnings("unused") @Cached("slot") TpSlotInquiryBuiltin cachedSlot,
@@ -159,13 +172,20 @@ static boolean callPython(VirtualFrame frame, TpSlotPythonSingle slot, Object se
@Specialization
static boolean callNative(VirtualFrame frame, Node inliningTarget, TpSlotNative slot, Object self,
@Cached GetThreadStateNode getThreadStateNode,
- @Cached(inline = false) PythonToNativeNode toNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) CheckInquiryResultNode checkResultNode) {
+ @Cached PythonToNativeInternalNode toNativeNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached CheckInquiryResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, T___BOOL__, slot.callable, toNativeNode.execute(self));
- return checkResultNode.executeBool(threadState, T___BOOL__, result);
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ try {
+ int iresult = ExternalFunctionInvoker.invokeINQUIRY(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, threadState, slot.callable,
+ toNativeNode.execute(inliningTarget, promotedSelf, false));
+ return checkResultNode.executeBool(inliningTarget, threadState, T___BOOL__, iresult);
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ }
}
@Specialization(replaces = "callCachedBuiltin")
@@ -209,4 +229,26 @@ static boolean doIt(VirtualFrame frame, TpSlotPythonSingle slot, Object self,
return pyObjectIsTrueNode.execute(frame, result);
}
}
+
+ /**
+ * Tests if the primitive result of the called function is {@code -1} and if an error occurred.
+ * In this case, the error is re-raised. Otherwise, it converts the result to a Boolean. This is
+ * equivalent to the result processing part in {@code Object/typeobject.c: wrap_inquirypred} and
+ * {@code Object/typeobject.c: wrap_objobjproc}.
+ */
+ @GenerateInline
+ @GenerateCached(false)
+ @GenerateUncached
+ abstract static class CheckInquiryResultNode extends Node {
+
+ public abstract boolean executeBool(Node inliningTarget, PythonThreadState threadState, TruffleString name, int result);
+
+ @Specialization
+ static boolean doLong(Node inliningTarget, PythonThreadState threadState, TruffleString name, int result,
+ @Cached InlinedConditionProfile resultProfile,
+ @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
+ transformExceptionFromNativeNode.execute(inliningTarget, threadState, name, result == -1, false);
+ return resultProfile.profile(inliningTarget, result != 0);
+ }
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotIterNext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotIterNext.java
index 52a2daef2f..59efb7052f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotIterNext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotIterNext.java
@@ -41,18 +41,20 @@
package com.oracle.graal.python.builtins.objects.type.slots;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___NEXT__;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.T___NEXT__;
+
+import java.lang.ref.Reference;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.annotations.Slot;
import com.oracle.graal.python.annotations.Slot.SlotKind;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes;
import com.oracle.graal.python.builtins.objects.function.PArguments;
@@ -61,15 +63,19 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotBuiltin;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNextFactory.CallSlotTpIterNextNodeGen;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotUnaryFunc.CallSlotUnaryPythonNode;
import com.oracle.graal.python.lib.IteratorExhausted;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -171,6 +177,11 @@ public abstract static class CallSlotTpIterNextNode extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self) {
+ return CallSlotTpIterNextNodeGen.getUncached().execute(null, null, slot, self);
+ }
+
@Specialization(guards = "cachedSlot == slot", limit = "3")
static Object callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotIterNextBuiltin> slot, Object self,
@SuppressWarnings("unused") @Cached("slot") TpSlotIterNextBuiltin> cachedSlot,
@@ -188,22 +199,29 @@ static Object callPython(VirtualFrame frame, TpSlotPythonSingle slot, Object sel
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self,
@Cached GetThreadStateNode getThreadStateNode,
@Cached(inline = false) PythonToNativeNode toNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Cached CExtCommonNodes.ReadAndClearNativeException readAndClearNativeException) {
- PythonThreadState state = getThreadStateNode.execute(inliningTarget);
- Object nativeResult = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T___NEXT__, slot.callable,
- toNativeNode.execute(self));
- Object pythonResult = toPythonNode.execute(nativeResult);
- if (pythonResult == PNone.NO_VALUE) {
- Object currentException = readAndClearNativeException.execute(inliningTarget, state);
- if (currentException != PNone.NO_VALUE) {
- throw PException.fromObjectFixUncachedLocation(currentException, inliningTarget, false);
- } else {
- throw TpIterNextBuiltin.iteratorExhausted();
+ PythonContext ctx = PythonContext.get(inliningTarget);
+ PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ try {
+ long nativeResult = ExternalFunctionInvoker.invokeITERNEXTFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ toNativeNode.executeLong(promotedSelf));
+ Object pythonResult = toPythonNode.execute(inliningTarget, nativeResult, true);
+ if (pythonResult == PNone.NO_VALUE) {
+ Object currentException = readAndClearNativeException.execute(inliningTarget, state);
+ if (currentException != PNone.NO_VALUE) {
+ throw PException.fromObjectFixUncachedLocation(currentException, inliningTarget, false);
+ } else {
+ throw TpIterNextBuiltin.iteratorExhausted();
+ }
}
+ return pythonResult;
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
}
- return pythonResult;
}
@Specialization(replaces = "callCachedBuiltin")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotLen.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotLen.java
index 47d2b6bd69..cb8fed064e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotLen.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotLen.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -45,9 +45,12 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___LEN__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___LEN__;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckPrimitiveFunctionResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
@@ -57,6 +60,7 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotBuiltinBase;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotLenFactory.CallSlotLenNodeGen;
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
import com.oracle.graal.python.lib.PyNumberIndexNode;
import com.oracle.graal.python.nodes.ErrorMessages;
@@ -65,10 +69,12 @@
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
import com.oracle.graal.python.nodes.util.CastToJavaIntLossyNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -150,6 +156,11 @@ public abstract static class CallSlotLenNode extends Node {
public abstract int execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self);
+ @TruffleBoundary
+ public static int executeUncached(TpSlot slot, Object self) {
+ return CallSlotLenNodeGen.getUncached().execute(null, null, slot, self);
+ }
+
@Specialization(guards = "cachedSlot == slot", limit = "3")
static int callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotLenBuiltin> slot, Object self,
@SuppressWarnings("unused") @Cached("slot") TpSlotLenBuiltin> cachedSlot,
@@ -166,18 +177,25 @@ static int callPython(VirtualFrame frame, TpSlotPythonSingle slot, Object self,
@Specialization
static int callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self,
@Exclusive @Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode toNativeNode,
- @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
@Exclusive @Cached PRaiseNode raiseNode,
- @Exclusive @Cached(inline = false) CheckPrimitiveFunctionResultNode checkResultNode) {
+ @Exclusive @Cached CheckPrimitiveFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T___LEN__, slot.callable, toNativeNode.execute(self));
- long l = checkResultNode.executeLong(state, T___LEN__, result);
- if (!PInt.isIntRange(l)) {
- raiseOverflow(inliningTarget, raiseNode, l);
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeLENFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ toNativeNode.executeLong(promotedSelf));
+ long l = checkResultNode.executeLong(inliningTarget, state, T___LEN__, lresult);
+ if (!PInt.isIntRange(l)) {
+ raiseOverflow(inliningTarget, raiseNode, l);
+ }
+ return (int) l;
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
}
- return (int) l;
}
@InliningCutoff
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotMpAssSubscript.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotMpAssSubscript.java
index 969e372f24..a7e57d6c06 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotMpAssSubscript.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotMpAssSubscript.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -45,14 +45,16 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___DELITEM__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SETITEM__;
+import java.lang.ref.Reference;
import java.util.Objects;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckInquiryResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.CheckInquiryResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
@@ -64,14 +66,18 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotManaged;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPython;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotMpAssSubscriptFactory.CallSlotMpAssSubscriptNodeGen;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -179,6 +185,11 @@ public Object getType() {
public abstract static class CallSlotMpAssSubscriptNode extends Node {
public abstract void execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self, Object key, Object value);
+ @TruffleBoundary
+ public static void executeUncached(TpSlot slot, Object self, Object key, Object value) {
+ CallSlotMpAssSubscriptNodeGen.getUncached().execute(null, null, slot, self, key, value);
+ }
+
@Specialization
static void callManagedSlot(VirtualFrame frame, Node inliningTarget, TpSlotManaged slot, Object self, Object key, Object value,
@Cached CallManagedSlotMpAssSubscriptNode slotNode) {
@@ -203,17 +214,27 @@ abstract static class CallNativeSlotMpAssSubscriptNode extends Node {
@Specialization
static void callNative(VirtualFrame frame, TpSlotNative slot, Object self, Object key, Object value,
@Bind Node inliningTarget,
+ @Bind PythonContext ctx,
@Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached PythonToNativeNode selfToNativeNode,
@Cached PythonToNativeNode keyToNativeNode,
@Cached PythonToNativeNode valueToNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
@Cached CheckInquiryResultNode checkResultNode) {
- Object result;
PythonThreadState threadState = getThreadStateNode.execute(inliningTarget);
- result = externalInvokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, T___SETITEM__, slot.callable,
- selfToNativeNode.execute(self), keyToNativeNode.execute(key), valueToNativeNode.execute(value));
- checkResultNode.execute(threadState, T___SETITEM__, result);
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ Object promotedKey = ensurePythonObjectNode.execute(ctx, key, false);
+ Object promotedValue = ensurePythonObjectNode.execute(ctx, value, false);
+ try {
+ int iresult = ExternalFunctionInvoker.invokeOBJOBJARGPROC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, threadState, slot.callable,
+ selfToNativeNode.executeLong(promotedSelf), keyToNativeNode.executeLong(promotedKey), valueToNativeNode.executeLong(promotedValue));
+ checkResultNode.executeBool(inliningTarget, threadState, T___SETITEM__, iresult);
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(promotedKey);
+ Reference.reachabilityFence(promotedValue);
+ }
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotNbPower.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotNbPower.java
index fe3611fd91..333af4b608 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotNbPower.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotNbPower.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -44,15 +44,18 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___IPOW__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___POW__;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.PNotImplemented;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
@@ -67,10 +70,15 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.CallReversiblePythonSlotNode;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.ReversibleSlot;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.TpSlotReversiblePython;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotNbPowerFactory.CallSlotNbInPlacePowerNodeGen;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotNbPowerFactory.CallSlotNbPowerNodeGen;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
+import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -146,6 +154,11 @@ public abstract static class CallSlotNbPowerNode extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlot slot,
Object v, Object vType, Object w, TpSlot wSlot, Object wType, Object z, boolean sameTypes);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object v, Object vType, Object w, TpSlot wSlot, Object wType, Object z, boolean sameTypes) {
+ return CallSlotNbPowerNodeGen.getUncached().execute(null, null, slot, v, vType, w, wSlot, wType, z, sameTypes);
+ }
+
@SuppressWarnings("unused")
@Specialization(guards = "cachedSlot == slot", limit = "3")
static Object callCachedBuiltin(VirtualFrame frame, TpSlotNbPowerBuiltin> slot,
@@ -167,17 +180,27 @@ static Object callPython(VirtualFrame frame, TpSlotReversiblePython slot,
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot,
Object v, Object vType, Object w, TpSlot wSlot, Object wType, Object z, boolean sameTypes,
@Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode vToNative,
@Cached(inline = false) PythonToNativeNode wToNative,
@Cached(inline = false) PythonToNativeNode zToNative,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonContext.PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T___POW__, slot.callable,
- vToNative.execute(v), wToNative.execute(w), zToNative.execute(z));
- return checkResultNode.execute(state, T___POW__, toPythonNode.execute(result));
+ Object promotedV = ensurePythonObjectNode.execute(ctx, v, false);
+ Object promotedW = ensurePythonObjectNode.execute(ctx, w, false);
+ Object promotedZ = ensurePythonObjectNode.execute(ctx, z, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeTERNARYFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ vToNative.executeLong(promotedV), wToNative.executeLong(promotedW), zToNative.executeLong(promotedZ));
+ return checkResultNode.execute(state, T___POW__, toPythonNode.execute(inliningTarget, lresult, true));
+ } finally {
+ Reference.reachabilityFence(promotedV);
+ Reference.reachabilityFence(promotedW);
+ Reference.reachabilityFence(promotedZ);
+ }
}
@SuppressWarnings("unused")
@@ -236,6 +259,11 @@ public abstract static class CallSlotNbInPlacePowerNode extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object v, Object w, Object z);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object v, Object w, Object z) {
+ return CallSlotNbInPlacePowerNodeGen.getUncached().execute(null, null, slot, v, w, z);
+ }
+
// There are no builtin implementations
@Specialization
@@ -248,17 +276,27 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi
@Specialization
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object v, Object w, Object z,
@Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode vToNative,
@Cached(inline = false) PythonToNativeNode wToNative,
@Cached(inline = false) PythonToNativeNode zToNative,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
- PythonContext.PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T___IPOW__, slot.callable,
- vToNative.execute(v), wToNative.execute(w), zToNative.execute(z));
- return checkResultNode.execute(state, T___IPOW__, toPythonNode.execute(result));
+ PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
+ Object promotedV = ensurePythonObjectNode.execute(ctx, v, false);
+ Object promotedW = ensurePythonObjectNode.execute(ctx, w, false);
+ Object promotedZ = ensurePythonObjectNode.execute(ctx, z, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeTERNARYFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ vToNative.executeLong(promotedV), wToNative.executeLong(promotedW), zToNative.executeLong(promotedZ));
+ return checkResultNode.execute(state, T___IPOW__, toPythonNode.execute(inliningTarget, lresult, true));
+ } finally {
+ Reference.reachabilityFence(promotedV);
+ Reference.reachabilityFence(promotedW);
+ Reference.reachabilityFence(promotedZ);
+ }
}
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRepr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRepr.java
index cb6a41f280..6380fb01b1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRepr.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRepr.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -42,11 +42,14 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___REPR__;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PFunction;
@@ -59,6 +62,8 @@
import com.oracle.graal.python.nodes.call.CallNode;
import com.oracle.graal.python.nodes.call.special.MaybeBindDescriptorNode;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.util.PythonUtils;
@@ -106,14 +111,21 @@ static Object callPython(VirtualFrame frame, TpSlotPythonSingle slot, Object sel
@Specialization
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self,
@Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode toNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
- PythonThreadState state = getThreadStateNode.execute(inliningTarget);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T___REPR__, slot.callable,
- toNativeNode.execute(self));
- return checkResultNode.execute(state, T___REPR__, toPythonNode.execute(result));
+ PythonContext ctx = PythonContext.get(inliningTarget);
+ PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeREPRFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ toNativeNode.executeLong(promotedSelf));
+ return checkResultNode.execute(state, T___REPR__, toPythonNode.execute(inliningTarget, lresult, true));
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ }
}
@Specialization(replaces = "callCachedBuiltin")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRichCompare.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRichCompare.java
index 6b05f997b9..c5cd3529e2 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRichCompare.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotRichCompare.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -43,17 +43,19 @@
import static com.oracle.graal.python.builtins.objects.type.slots.BuiltinSlotWrapperSignature.J_DOLLAR_SELF;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J_TP_RICHCOMPARE;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T_TP_RICHCOMPARE;
-import static com.oracle.graal.python.nodes.SpecialMethodNames.T___HASH__;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.PNotImplemented;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
@@ -62,10 +64,12 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotBuiltinBase;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPython;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompareFactory.CallSlotRichCmpNodeGen;
import com.oracle.graal.python.lib.RichCmpOp;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
@@ -209,6 +213,11 @@ public abstract static class CallSlotRichCmpNode extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object a, Object b, RichCmpOp op);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object a, Object b, RichCmpOp op) {
+ return CallSlotRichCmpNodeGen.getUncached().execute(null, null, slot, a, b, op);
+ }
+
@Specialization(guards = "cachedSlot == slot", limit = "3")
static Object callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotRichCmpBuiltin> slot, Object a, Object b, RichCmpOp op,
@SuppressWarnings("unused") @Cached("slot") TpSlotRichCmpBuiltin> cachedSlot,
@@ -243,14 +252,22 @@ static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNati
@Exclusive @Cached GetThreadStateNode getThreadStateNode,
@Cached(inline = false) PythonToNativeNode toNativeNodeA,
@Cached(inline = false) PythonToNativeNode toNativeNodeB,
- @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Exclusive @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Exclusive @Cached NativeToPythonInternalNode toPythonNode,
@Exclusive @Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T_TP_RICHCOMPARE, slot.callable, toNativeNodeA.execute(a), toNativeNodeB.execute(b),
- op.asNative());
- return checkResultNode.execute(state, T___HASH__, toPythonNode.execute(result));
+ Object promotedA = ensurePythonObjectNode.execute(ctx, a, false);
+ Object promotedB = ensurePythonObjectNode.execute(ctx, b, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeRICHCMPFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ toNativeNodeA.executeLong(promotedA), toNativeNodeB.executeLong(promotedB), op.asNative());
+ return checkResultNode.execute(state, T_TP_RICHCOMPARE, toPythonNode.execute(inliningTarget, lresult, true));
+ } finally {
+ Reference.reachabilityFence(promotedA);
+ Reference.reachabilityFence(promotedB);
+ }
}
@Specialization(replaces = "callCachedBuiltin")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSetAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSetAttr.java
index 118f70b3c2..81f50bee0a 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSetAttr.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSetAttr.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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,23 +41,26 @@
package com.oracle.graal.python.builtins.objects.type.slots;
import static com.oracle.graal.python.builtins.objects.type.slots.BuiltinSlotWrapperSignature.J_DOLLAR_SELF;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.free;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___SETATTR__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___DELATTR__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SETATTR__;
import java.lang.ref.Reference;
+import java.util.logging.Level;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.PNone;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.AsCharPointerNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckInquiryResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.CheckInquiryResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.FreeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
@@ -68,17 +71,23 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotManaged;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPython;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSetAttrFactory.CallManagedSlotSetAttrNodeGen;
import com.oracle.graal.python.lib.PyUnicodeCheckNode;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode.Dynamic;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.graal.python.util.PythonUtils;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
@@ -250,6 +259,7 @@ static void callNative(VirtualFrame frame, TpSlots slots, TpSlotNative slot, Obj
@GenerateInline(false) // Used lazily
@GenerateUncached
abstract static class CallNativeSlotSetAttrNode extends Node {
+ private static final TruffleLogger LOGGER = CApiContext.getLogger(CallNativeSlotSetAttrNode.class);
private static final CApiTiming C_API_TIMING = CApiTiming.create(true, "tp_setattr");
// The caller should ensure that the "name" is a Unicode object (subclasses are permitted)
@@ -258,36 +268,46 @@ abstract static class CallNativeSlotSetAttrNode extends Node {
@Specialization
static void callNative(VirtualFrame frame, TpSlots slots, TpSlotNative slot, Object self, Object name, Object value,
@Bind Node inliningTarget,
+ @Bind PythonContext context,
@Cached GetThreadStateNode getThreadStateNode,
@Cached InlinedConditionProfile isSetAttrProfile,
@Cached AsCharPointerNode asCharPointerNode,
- @Cached FreeNode freeNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached PythonToNativeNode nameToNativeNode,
@Cached PythonToNativeNode selfToNativeNode,
@Cached PythonToNativeNode valueToNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
@Cached CheckInquiryResultNode checkResultNode) {
assert PyUnicodeCheckNode.executeUncached(name);
boolean isSetAttr = isSetAttrProfile.profile(inliningTarget, slots.tp_setattr() == slot);
- Object nameArg;
+ Object promotedSelf = ensurePythonObjectNode.execute(context, self, false);
+ Object promotedName = null;
+ long nameArg;
if (isSetAttr) {
nameArg = asCharPointerNode.execute(name);
} else {
- nameArg = nameToNativeNode.execute(name);
+ promotedName = ensurePythonObjectNode.execute(context, name, false);
+ nameArg = nameToNativeNode.executeLong(promotedName);
}
- Object result;
- PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, null);
+ Object promotedValue = ensurePythonObjectNode.execute(context, value, false);
+ int iresult;
+ PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, context);
try {
- result = externalInvokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, T___SETATTR__, slot.callable, selfToNativeNode.execute(self), nameArg,
- valueToNativeNode.execute(value));
+ iresult = ExternalFunctionInvoker.invokeSETATTRFUNC(frame, C_API_TIMING, context.ensureNativeContext(), boundaryCallData, threadState, slot.callable,
+ selfToNativeNode.executeLong(promotedSelf), nameArg, valueToNativeNode.executeLong(promotedValue));
} finally {
+ Reference.reachabilityFence(promotedSelf);
if (isSetAttr) {
- freeNode.free(nameArg);
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(PythonUtils.formatJString("Freeing name (const char *)0x%x", nameArg));
+ }
+ free(nameArg);
} else {
- Reference.reachabilityFence(nameArg);
+ Reference.reachabilityFence(promotedName);
}
+ Reference.reachabilityFence(promotedValue);
}
- checkResultNode.execute(threadState, T___SETATTR__, result);
+ checkResultNode.executeBool(inliningTarget, threadState, T___SETATTR__, iresult);
}
}
@@ -310,6 +330,11 @@ public abstract static class CallManagedSlotSetAttrNode extends Node {
public abstract void execute(VirtualFrame frame, Node inliningTarget, TpSlotManaged slot, Object self, TruffleString name, Object value);
+ @TruffleBoundary
+ public static void executeUncached(TpSlotManaged slot, Object self, Object name, Object value) {
+ CallManagedSlotSetAttrNodeGen.getUncached().execute(null, null, slot, self, name, value);
+ }
+
@Specialization(guards = "slot == cachedSlot", limit = "3")
static void callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotSetAttrBuiltin slot, Object self, TruffleString name, Object value,
@SuppressWarnings("unused") @Cached("slot") TpSlotSetAttrBuiltin cachedSlot,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSizeArgFun.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSizeArgFun.java
index 3617b0d385..23e641aaf9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSizeArgFun.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSizeArgFun.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -42,16 +42,18 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETITEM__;
+import java.lang.ref.Reference;
import java.util.Objects;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
@@ -63,16 +65,19 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotLen.CallSlotLenNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFunFactory.CallSlotSizeArgFunNodeGen;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFunFactory.FixNegativeIndexNodeGen;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFunFactory.WrapIndexArgFuncBuiltinNodeGen;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFunFactory.WrapSqItemBuiltinNodeGen;
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -114,9 +119,9 @@ public void initialize(PythonLanguage language) {
@Override
public PBuiltinFunction createBuiltin(Python3Core core, Object type, TruffleString tsName, PExternalFunctionWrapper wrapper) {
return switch (wrapper) {
- case GETITEM -> createBuiltin(core, type, tsName, BuiltinSlotWrapperSignature.BINARY, wrapper,
+ case SQ_ITEM -> createBuiltin(core, type, tsName, BuiltinSlotWrapperSignature.BINARY, wrapper,
WrapperNodeFactory.wrap(getNodeFactory(), WrapSqItemBuiltinNode.class, WrapSqItemBuiltinNodeGen::create));
- case SSIZE_ARG -> createBuiltin(core, type, tsName, BuiltinSlotWrapperSignature.BINARY, wrapper,
+ case INDEXARGFUNC -> createBuiltin(core, type, tsName, BuiltinSlotWrapperSignature.BINARY, wrapper,
WrapperNodeFactory.wrap(getNodeFactory(), WrapIndexArgFuncBuiltinNode.class,
WrapIndexArgFuncBuiltinNodeGen::create));
default ->
@@ -236,6 +241,11 @@ public abstract static class CallSlotSizeArgFun extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self, int index);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self, int index) {
+ return CallSlotSizeArgFunNodeGen.getUncached().execute(null, null, slot, self, index);
+ }
+
@Specialization(guards = "cachedSlot == slot", limit = "3")
static Object callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotSizeArgFunBuiltin> slot, Object self, int index,
@SuppressWarnings("unused") @Cached("slot") TpSlotSizeArgFunBuiltin> cachedSlot,
@@ -252,14 +262,21 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi
@Specialization
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self, int index,
@Exclusive @Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode toNativeNode,
- @Exclusive @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Exclusive @Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, T___GETITEM__, slot.callable, toNativeNode.execute(self), index);
- return checkResultNode.execute(threadState, T___GETITEM__, toPythonNode.execute(result));
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeSSIZEARGFUNC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, threadState, slot.callable,
+ toNativeNode.executeLong(promotedSelf), index);
+ return checkResultNode.execute(threadState, T___GETITEM__, toPythonNode.execute(inliningTarget, lresult, true));
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ }
}
@Specialization(replaces = "callCachedBuiltin")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqAssItem.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqAssItem.java
index f38f2cfbef..f818060fdc 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqAssItem.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqAssItem.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -45,14 +45,16 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___DELITEM__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SETITEM__;
+import java.lang.ref.Reference;
import java.util.Objects;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckInquiryResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.CheckInquiryResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
@@ -66,6 +68,7 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPython;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSizeArgFun.FixNegativeIndex;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqAssItemFactory.CallSlotSqAssItemNodeGen;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqAssItemFactory.WrapSqDelItemBuiltinNodeGen;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqAssItemFactory.WrapSqSetItemBuiltinNodeGen;
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
@@ -75,10 +78,13 @@
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -120,9 +126,9 @@ public void initialize(PythonLanguage language) {
@Override
public PBuiltinFunction createBuiltin(Python3Core core, Object type, TruffleString tsName, PExternalFunctionWrapper wrapper) {
return switch (wrapper) {
- case SETITEM -> createBuiltin(core, type, T___SETITEM__, SET_SIGNATURE, wrapper,
+ case SQ_SETITEM -> createBuiltin(core, type, T___SETITEM__, SET_SIGNATURE, wrapper,
WrapperNodeFactory.wrap(getNodeFactory(), WrapSqSetItemBuiltinNode.class, WrapSqSetItemBuiltinNodeGen::create));
- case DELITEM -> createBuiltin(core, type, T___DELITEM__, BuiltinSlotWrapperSignature.BINARY, wrapper,
+ case SQ_DELITEM -> createBuiltin(core, type, T___DELITEM__, BuiltinSlotWrapperSignature.BINARY, wrapper,
WrapperNodeFactory.wrap(getNodeFactory(), WrapSqDelItemBuiltinNode.class, WrapSqDelItemBuiltinNodeGen::create));
default ->
throw new IllegalStateException(Objects.toString(wrapper));
@@ -257,6 +263,11 @@ public Object getType() {
public abstract static class CallSlotSqAssItemNode extends Node {
public abstract void execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self, int key, Object value);
+ @TruffleBoundary
+ public static void executeUncached(TpSlot slot, Object self, int key, Object value) {
+ CallSlotSqAssItemNodeGen.getUncached().execute(null, null, slot, self, key, value);
+ }
+
@Specialization
static void callManagedSlot(VirtualFrame frame, Node inliningTarget, TpSlotManaged slot, Object self, int key, Object value,
@Cached CallManagedSlotSqAssItemNode slotNode) {
@@ -281,16 +292,24 @@ abstract static class CallNativeSlotSqAssItemNode extends Node {
@Specialization
static void callNative(VirtualFrame frame, TpSlotNative slot, Object self, long key, Object value,
@Bind Node inliningTarget,
+ @Bind PythonContext context,
@Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached PythonToNativeNode selfToNativeNode,
@Cached PythonToNativeNode valueToNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
@Cached CheckInquiryResultNode checkResultNode) {
- Object result;
- PythonThreadState threadState = getThreadStateNode.execute(inliningTarget);
- result = externalInvokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, T___SETITEM__, slot.callable,
- selfToNativeNode.execute(self), key, valueToNativeNode.execute(value));
- checkResultNode.execute(threadState, T___SETITEM__, result);
+ PythonThreadState threadState = getThreadStateNode.execute(inliningTarget, context);
+ Object promotedSelf = ensurePythonObjectNode.execute(context, self, false);
+ Object promotedValue = ensurePythonObjectNode.execute(context, value, false);
+ try {
+ int iresult = ExternalFunctionInvoker.invokeSSIZEOBJARGPROC(frame, C_API_TIMING, context.ensureNativeContext(), boundaryCallData, threadState, slot.callable,
+ selfToNativeNode.executeLong(promotedSelf), key, valueToNativeNode.executeLong(promotedValue));
+ checkResultNode.executeBool(inliningTarget, threadState, T___SETITEM__, iresult);
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(promotedValue);
+ }
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqContains.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqContains.java
index 9a6fba5593..5e715944c4 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqContains.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotSqContains.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -43,27 +43,33 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___CONTAINS__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___CONTAINS__;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckInquiryResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.type.slots.PythonDispatchers.BinaryPythonSlotDispatcherNode;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryFunc.TpSlotBinaryFuncBuiltin;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.CheckInquiryResultNode;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqContainsFactory.CallSlotSqContainsNodeGen;
import com.oracle.graal.python.lib.PyObjectIsTrueNode;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Cached;
@@ -108,6 +114,11 @@ public abstract static class CallSlotSqContainsNode extends Node {
public abstract boolean execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self, Object arg);
+ @TruffleBoundary
+ public static boolean executeUncached(TpSlot slot, Object self, Object arg) {
+ return CallSlotSqContainsNodeGen.getUncached().execute(null, null, slot, self, arg);
+ }
+
@Specialization(guards = "cachedSlot == slot", limit = "3")
static boolean callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotSqContainsBuiltin> slot, Object self, Object arg,
@SuppressWarnings("unused") @Cached("slot") TpSlotSqContainsBuiltin> cachedSlot,
@@ -130,15 +141,24 @@ static boolean callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonS
@Specialization
static boolean callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self, Object arg,
@Cached GetThreadStateNode getThreadStateNode,
- @Cached(inline = false) PythonToNativeNode selfToNativeNode,
- @Cached(inline = false) PythonToNativeNode argToNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) CheckInquiryResultNode checkResultNode) {
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached PythonToNativeInternalNode selfToNativeNode,
+ @Cached PythonToNativeInternalNode argToNativeNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached CheckInquiryResultNode checkResultNode) {
PythonContext ctx = PythonContext.get(inliningTarget);
PythonThreadState state = getThreadStateNode.execute(inliningTarget, ctx);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T___CONTAINS__, slot.callable,
- selfToNativeNode.execute(self), argToNativeNode.execute(arg));
- return checkResultNode.executeBool(state, T___CONTAINS__, result);
+ Object promotedSelf = ensurePythonObjectNode.execute(ctx, self, false);
+ Object promotedArg = ensurePythonObjectNode.execute(ctx, arg, false);
+ try {
+ int iresult = ExternalFunctionInvoker.invokeOBJOBJPROC(frame, C_API_TIMING, ctx.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ selfToNativeNode.execute(inliningTarget, promotedSelf, false),
+ argToNativeNode.execute(inliningTarget, promotedArg, false));
+ return checkResultNode.executeBool(inliningTarget, state, T___CONTAINS__, iresult);
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(promotedArg);
+ }
}
@Specialization(replaces = "callCachedBuiltin")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotUnaryFunc.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotUnaryFunc.java
index 376c629f23..04ed4ec5d5 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotUnaryFunc.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotUnaryFunc.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -42,22 +42,29 @@
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.function.PArguments;
import com.oracle.graal.python.builtins.objects.type.slots.PythonDispatchers.UnaryPythonSlotDispatcherNode;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotBuiltinBase;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotUnaryFuncFactory.CallSlotUnaryNodeGen;
import com.oracle.graal.python.nodes.call.CallDispatchers;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.dsl.Bind;
@@ -104,6 +111,11 @@ public abstract static class CallSlotUnaryNode extends Node {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, TpSlot slot, Object self);
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self) {
+ return CallSlotUnaryNodeGen.getUncached().execute(null, null, slot, self);
+ }
+
@Specialization(guards = "cachedSlot == slot", limit = "3")
static Object callCachedBuiltin(VirtualFrame frame, @SuppressWarnings("unused") TpSlotUnaryFuncBuiltin> slot, Object self,
@SuppressWarnings("unused") @Cached("slot") TpSlotUnaryFuncBuiltin> cachedSlot,
@@ -119,15 +131,22 @@ static Object callPython(VirtualFrame frame, TpSlotPythonSingle slot, Object sel
@Specialization
static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self,
+ @Bind PythonContext context,
@Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
@Cached(inline = false) PythonToNativeNode toNativeNode,
- @Cached ExternalFunctionInvokeNode externalInvokeNode,
- @Cached(inline = false) NativeToPythonTransferNode toPythonNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached NativeToPythonInternalNode toPythonNode,
@Cached(inline = false) PyObjectCheckFunctionResultNode checkResultNode) {
- PythonThreadState state = getThreadStateNode.execute(inliningTarget);
- Object result = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, T_UNARY_SLOT, slot.callable,
- toNativeNode.execute(self));
- return checkResultNode.execute(state, T_UNARY_SLOT, toPythonNode.execute(result));
+ PythonThreadState state = getThreadStateNode.execute(inliningTarget, context);
+ Object promotedSelf = ensurePythonObjectNode.execute(context, self, false);
+ try {
+ long lresult = ExternalFunctionInvoker.invokeUNARYFUNC(frame, C_API_TIMING, context.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ toNativeNode.executeLong(promotedSelf));
+ return checkResultNode.execute(state, T_UNARY_SLOT, toPythonNode.execute(inliningTarget, lresult, true, true));
+ } finally {
+ Reference.reachabilityFence(promotedSelf);
+ }
}
@Specialization(replaces = "callCachedBuiltin")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java
index 6063a8998b..43f64bfb6e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -47,22 +47,25 @@
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___NEW__;
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
+import java.lang.ref.Reference;
+
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.annotations.Slot.SlotSignature;
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.PythonBuiltins;
import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CreateArgsTupleNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.DefaultCheckFunctionResultNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CreateNativeArgsTupleNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.EagerTupleState;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.InitCheckFunctionResultNode;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PyObjectCheckFunctionResultNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ReleaseNativeArgsTupleNode;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonTransferNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CheckFunctionResultNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
+import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformExceptionFromNativeNode;
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
import com.oracle.graal.python.builtins.objects.function.PFunction;
import com.oracle.graal.python.builtins.objects.function.PKeyword;
@@ -76,7 +79,11 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotCExtNative;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpCallNodeGen;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpInitNodeGen;
+import com.oracle.graal.python.builtins.objects.type.slots.TpSlotVarargsFactory.CallSlotTpNewNodeGen;
import com.oracle.graal.python.nodes.ErrorMessages;
+import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.argument.CreateArgumentsNode;
import com.oracle.graal.python.nodes.call.BoundDescriptor;
@@ -90,12 +97,14 @@
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;
import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
@@ -105,6 +114,7 @@
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateUncached;
+import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
@@ -306,33 +316,54 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi
@GenerateInline(false)
@GenerateUncached
- abstract static class CallSlotVarargsNativeNode extends Node {
+ abstract static class CallNativeTernaryfuncNode extends Node {
- abstract Object execute(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords, TruffleString name, CheckFunctionResultNode checkResultNode,
- NativeToPythonTransferNode toPythonNode);
+ abstract Object execute(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords, TruffleString name);
@Specialization
- static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords, TruffleString name, CheckFunctionResultNode checkResultNode,
- NativeToPythonTransferNode toPythonNode,
+ static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords, TruffleString name,
@Bind Node inliningTarget,
@Bind PythonContext context,
@Cached GetThreadStateNode getThreadStateNode,
- @Cached PythonToNativeNode toNativeNode,
- @Cached CreateArgsTupleNode createArgsTupleNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached PythonToNativeInternalNode toNativeNode,
+ @Cached CreateNativeArgsTupleNode createNativeArgsTupleNode,
+ @Cached ReleaseNativeArgsTupleNode releaseNativeArgsTupleNode,
@Cached EagerTupleState eagerTupleState,
- @Cached ExternalFunctionInvokeNode externalInvokeNode) {
+ @Cached NativeToPythonInternalNode toPythonNode,
+ @Cached PyObjectCheckFunctionResultNode checkResultNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData) {
PythonLanguage language = context.getLanguage(inliningTarget);
PythonThreadState state = getThreadStateNode.execute(inliningTarget, context);
- PTuple argsTuple = createArgsTupleNode.execute(inliningTarget, language, args, eagerTupleState);
+ Object promotedSelf = ensurePythonObjectNode.execute(context, self, false);
+ PTuple managedArgsTuple;
+ long argsTuplePtr;
+ if (eagerTupleState.isEager(inliningTarget)) {
+ managedArgsTuple = null;
+ argsTuplePtr = createNativeArgsTupleNode.execute(context, args);
+ } else {
+ managedArgsTuple = PFactory.createTuple(context.getLanguage(inliningTarget), args);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(managedArgsTuple);
+ argsTuplePtr = toNativeNode.execute(inliningTarget, managedArgsTuple, false);
+ }
Object kwargsDict = keywords.length > 0 ? PFactory.createDict(language, keywords) : NO_VALUE;
- Object nativeResult = externalInvokeNode.call(frame, inliningTarget, state, C_API_TIMING, name, slot.callable,
- toNativeNode.execute(self), toNativeNode.execute(argsTuple), toNativeNode.execute(kwargsDict));
- eagerTupleState.report(inliningTarget, argsTuple);
- checkResultNode.execute(state, name, nativeResult);
- if (toPythonNode != null) {
- return toPythonNode.execute(nativeResult);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(kwargsDict);
+ try {
+ long nativeResult = ExternalFunctionInvoker.invokeTERNARYFUNC(frame, C_API_TIMING, context.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ toNativeNode.execute(inliningTarget, promotedSelf, false),
+ argsTuplePtr,
+ toNativeNode.execute(inliningTarget, kwargsDict, false));
+ return checkResultNode.execute(state, name, toPythonNode.execute(inliningTarget, nativeResult, true, true));
+ } finally {
+ if (managedArgsTuple != null) {
+ eagerTupleState.report(inliningTarget, managedArgsTuple);
+ } else {
+ releaseNativeArgsTupleNode.execute(argsTuplePtr, args);
+ }
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(args);
+ Reference.reachabilityFence(kwargsDict);
}
- return NO_VALUE;
}
}
@@ -342,6 +373,11 @@ static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self,
@ReportPolymorphism
public abstract static class CallSlotTpInitNode extends CallVarargsTpSlotBaseNode {
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self, Object[] args, PKeyword[] keywords) {
+ return CallSlotTpInitNodeGen.getUncached().execute(null, null, slot, self, args, keywords);
+ }
+
@Specialization
static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
@Cached CallSlotVarargsPythonNode callNode,
@@ -355,10 +391,48 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi
@Specialization
@InliningCutoff
- static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords,
- @Cached InitCheckFunctionResultNode checkResult,
- @Cached CallSlotVarargsNativeNode callNode) {
- return callNode.execute(frame, slot, self, args, keywords, T___INIT__, checkResult, null);
+ static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords,
+ @Bind PythonContext context,
+ @Cached GetThreadStateNode getThreadStateNode,
+ @Cached EnsurePythonObjectNode ensurePythonObjectNode,
+ @Cached PythonToNativeInternalNode toNativeNode,
+ @Cached CreateNativeArgsTupleNode createNativeArgsTupleNode,
+ @Cached ReleaseNativeArgsTupleNode releaseNativeArgsTupleNode,
+ @Cached EagerTupleState eagerTupleState,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData,
+ @Cached InitCheckFunctionResultNode checkResultNode) {
+ PythonLanguage language = context.getLanguage(inliningTarget);
+ PythonThreadState state = getThreadStateNode.execute(inliningTarget, context);
+ Object promotedSelf = ensurePythonObjectNode.execute(context, self, false);
+ PTuple managedArgsTuple;
+ long argsTuplePtr;
+ if (eagerTupleState.isEager(inliningTarget)) {
+ managedArgsTuple = null;
+ argsTuplePtr = createNativeArgsTupleNode.execute(context, args);
+ } else {
+ managedArgsTuple = PFactory.createTuple(context.getLanguage(inliningTarget), args);
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(managedArgsTuple);
+ argsTuplePtr = toNativeNode.execute(inliningTarget, managedArgsTuple, false);
+ }
+ Object kwargsDict = keywords.length > 0 ? PFactory.createDict(language, keywords) : NO_VALUE;
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(kwargsDict);
+ try {
+ int nativeResult = ExternalFunctionInvoker.invokeINITPROC(frame, C_API_TIMING, context.ensureNativeContext(), boundaryCallData, state, slot.callable,
+ toNativeNode.execute(inliningTarget, promotedSelf, false),
+ argsTuplePtr,
+ toNativeNode.execute(inliningTarget, kwargsDict, false));
+ checkResultNode.executeInt(inliningTarget, state, T___INIT__, nativeResult);
+ } finally {
+ if (managedArgsTuple != null) {
+ eagerTupleState.report(inliningTarget, managedArgsTuple);
+ } else {
+ releaseNativeArgsTupleNode.execute(argsTuplePtr, args);
+ }
+ Reference.reachabilityFence(promotedSelf);
+ Reference.reachabilityFence(args);
+ Reference.reachabilityFence(kwargsDict);
+ }
+ return NO_VALUE;
}
}
@@ -405,6 +479,11 @@ static Object doBind(VirtualFrame frame, Node inliningTarget, Object descriptor,
@ReportPolymorphism
public abstract static class CallSlotTpNewNode extends CallVarargsTpSlotBaseNode {
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self, Object[] args, PKeyword[] keywords) {
+ return CallSlotTpNewNodeGen.getUncached().execute(null, null, slot, self, args, keywords);
+ }
+
@Specialization
static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
@Cached BindNewMethodNode bindNew,
@@ -416,10 +495,9 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi
@Specialization
@InliningCutoff
static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords,
- @Cached DefaultCheckFunctionResultNode checkResult,
- @Cached NativeToPythonTransferNode toPythonNode,
- @Cached CallSlotVarargsNativeNode callNode) {
- return callNode.execute(frame, slot, self, args, keywords, T___NEW__, checkResult, toPythonNode);
+ @Cached CallNativeTernaryfuncNode callNode) {
+ // 'tp_new' slot's C type is actually 'newfunc' but that is compatible to 'ternaryfunc'.
+ return callNode.execute(frame, slot, self, args, keywords, T___NEW__);
}
}
@@ -429,6 +507,11 @@ static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self,
@ReportPolymorphism
public abstract static class CallSlotTpCallNode extends CallVarargsTpSlotBaseNode {
+ @TruffleBoundary
+ public static Object executeUncached(TpSlot slot, Object self, Object[] args, PKeyword[] keywords) {
+ return CallSlotTpCallNodeGen.getUncached().execute(null, null, slot, self, args, keywords);
+ }
+
@Specialization
static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSingle slot, Object self, Object[] args, PKeyword[] keywords,
@Cached CallSlotVarargsPythonNode callNode) {
@@ -438,10 +521,36 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi
@Specialization
@InliningCutoff
static Object callNative(VirtualFrame frame, TpSlotCExtNative slot, Object self, Object[] args, PKeyword[] keywords,
- @Cached DefaultCheckFunctionResultNode checkResult,
- @Cached NativeToPythonTransferNode toPythonNode,
- @Cached CallSlotVarargsNativeNode callNode) {
- return callNode.execute(frame, slot, self, args, keywords, T___CALL__, checkResult, toPythonNode);
+ @Cached CallNativeTernaryfuncNode callNode) {
+ return callNode.execute(frame, slot, self, args, keywords, T___CALL__);
+ }
+ }
+
+ /**
+ * Processes the function result with CPython semantics:
+ *
+ *
+ *
+ * This is the case for {@code wrap_init}, {@code wrap_descr_delete}, {@code wrap_descr_set},
+ * {@code wrap_delattr}, {@code wrap_setattr}.
+ */
+ @ImportStatic(PGuards.class)
+ @GenerateInline
+ @GenerateUncached
+ @GenerateCached(false)
+ abstract static class InitCheckFunctionResultNode extends Node {
+
+ public abstract PNone executeInt(Node inliningTarget, PythonThreadState threadState, TruffleString name, int result);
+
+ @Specialization
+ static PNone doGeneric(Node inliningTarget, PythonThreadState state, TruffleString name, int result,
+ @Cached TransformExceptionFromNativeNode transformExceptionFromNativeNode) {
+ transformExceptionFromNativeNode.execute(inliningTarget, state, name, result < 0, true);
+ return PNone.NONE;
}
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyContextCopyCurrent.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyContextCopyCurrent.java
index 8f74711000..56bb1cefad 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyContextCopyCurrent.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyContextCopyCurrent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -47,7 +47,9 @@
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
+import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.nodes.Node;
/**
@@ -55,7 +57,13 @@
*/
@GenerateInline
@GenerateCached(false)
+@GenerateUncached
public abstract class PyContextCopyCurrent extends PNodeWithContext {
+ @TruffleBoundary
+ public static PContextVarsContext executeUncached() {
+ return PyContextCopyCurrentNodeGen.getUncached().execute(null);
+ }
+
public abstract PContextVarsContext execute(Node inliningTarget);
@Specialization
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyEvalGetGlobals.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyEvalGetGlobals.java
index 2de8698ee1..845c48131a 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyEvalGetGlobals.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyEvalGetGlobals.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -51,6 +51,7 @@
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
+import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -61,10 +62,15 @@
*/
@GenerateInline
@GenerateCached(false)
+@GenerateUncached
@ImportStatic(PArguments.class)
public abstract class PyEvalGetGlobals extends Node {
public abstract PythonObject execute(VirtualFrame frame, Node inliningTarget);
+ public static PythonObject executeUncached(VirtualFrame frame) {
+ return PyEvalGetGlobalsNodeGen.getUncached().execute(frame, null);
+ }
+
@Specialization(guards = {"frame != null", "globals != null"})
static PythonObject doFromFrame(@SuppressWarnings("unused") VirtualFrame frame, Node inliningTarget,
@Bind("getGlobals(frame)") PythonObject globals,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyFloatAsDoubleNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyFloatAsDoubleNode.java
index 3fbfc560a0..bd228659f9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyFloatAsDoubleNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyFloatAsDoubleNode.java
@@ -43,13 +43,13 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.DeprecationWarning;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyFloatObject__ob_fval;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readDoubleField;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___FLOAT__;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromNativeSubclassNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.floats.PFloat;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode;
@@ -119,9 +119,8 @@ static double doBoolean(boolean object) {
@InliningCutoff
static double doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject object,
@SuppressWarnings("unused") @Exclusive @Cached GetClassNode getClassNode,
- @SuppressWarnings("unused") @Exclusive @Cached(inline = false) IsSubtypeNode isSubtype,
- @Cached(inline = false) CStructAccess.ReadDoubleNode read) {
- return read.readFromObj(object, PyFloatObject__ob_fval);
+ @SuppressWarnings("unused") @Exclusive @Cached(inline = false) IsSubtypeNode isSubtype) {
+ return readDoubleField(object.getPtr(), PyFloatObject__ob_fval);
}
@Fallback
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongAsLongAndOverflowNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongAsLongAndOverflowNode.java
index 679553a0f8..49ee94f203 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongAsLongAndOverflowNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongAsLongAndOverflowNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -40,7 +40,6 @@
*/
package com.oracle.graal.python.lib;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.util.OverflowException;
@@ -91,11 +90,6 @@ static long doBoolean(boolean x) {
return x ? 1 : 0;
}
- @Specialization
- static long doNativePointer(PythonNativeVoidPtr object) {
- return object.getNativePointer();
- }
-
// TODO When we implement casting native longs, this should cast them instead of calling their
// __index__
@Fallback
@@ -139,10 +133,5 @@ static long doPInt(PInt object) throws OverflowException {
static long doBoolean(boolean x) {
return x ? 1 : 0;
}
-
- @Specialization
- static long doNativePointer(PythonNativeVoidPtr object) {
- return object.getNativePointer();
- }
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCheckExactNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCheckExactNode.java
index 3f2581df81..96ccd99830 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCheckExactNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCheckExactNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -40,7 +40,6 @@
*/
package com.oracle.graal.python.lib;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.truffle.api.dsl.Fallback;
@@ -78,11 +77,6 @@ static boolean doBuiltinPInt(@SuppressWarnings("unused") PInt object) {
return true;
}
- @Specialization
- static boolean doNativePtr(@SuppressWarnings("unused") PythonNativeVoidPtr object) {
- return true;
- }
-
@Fallback
static boolean doOther(@SuppressWarnings("unused") Object object) {
return false;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCheckNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCheckNode.java
index a63ff2b290..20e4a72fd8 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCheckNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCheckNode.java
@@ -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
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCopy.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCopy.java
index b894177240..eb84266be2 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCopy.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyLongCopy.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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,7 +41,6 @@
package com.oracle.graal.python.lib;
import com.oracle.graal.python.PythonLanguage;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.runtime.object.PFactory;
@@ -100,9 +99,4 @@ static PInt doPIntOverriden(PInt obj,
@Bind PythonLanguage language) {
return PFactory.createInt(language, obj.getValue());
}
-
- @Specialization
- static PythonNativeVoidPtr doL(PythonNativeVoidPtr obj) {
- return obj;
- }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberFloatNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberFloatNode.java
index a4d3e6b434..e440c94c45 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberFloatNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyNumberFloatNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -46,6 +46,7 @@
import com.oracle.graal.python.lib.PyFloatAsDoubleNode.HandleFloatResultNode;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.object.GetClassNode;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
@@ -74,6 +75,11 @@ public final double execute(Node inliningTarget, Object object) {
return execute(null, inliningTarget, object);
}
+ @TruffleBoundary
+ public static double executeUncached(Object object) {
+ return PyNumberFloatNodeGen.getUncached().execute(null, null, object);
+ }
+
@Specialization
static double doDouble(double object) {
return object;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectHashNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectHashNode.java
index b6567c96f4..a67d2a56cf 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectHashNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectHashNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -42,17 +42,19 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import com.oracle.graal.python.builtins.modules.MathModuleBuiltins;
import com.oracle.graal.python.builtins.modules.SysModuleBuiltins;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.ReadI64Node;
import com.oracle.graal.python.builtins.objects.str.PString;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode;
@@ -66,6 +68,7 @@
import com.oracle.graal.python.nodes.util.CastToTruffleStringNode;
import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext;
import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
+import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
@@ -86,6 +89,8 @@
@GenerateCached(false)
@GenerateInline
public abstract class PyObjectHashNode extends PNodeWithContext {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_PY_TYPE_READY);
+
public static long executeUncached(Object value) {
return PyObjectHashNodeGen.getUncached().execute(null, null, value);
}
@@ -171,7 +176,6 @@ public static long hash(double object) {
static long genericHash(VirtualFrame frame, Node inliningTarget, Object object,
@Cached GetClassNode getClassNode,
@Cached GetCachedTpSlotsNode getSlotsNode,
- @Cached CStructAccess.ReadI64Node readTypeObjectFieldNode,
@Cached InlinedConditionProfile typeIsNotReadyProfile,
@Cached("createFor($node)") BoundaryCallData boundaryCallData,
@Cached CallSlotHashFunNode callHashFun,
@@ -179,15 +183,13 @@ static long genericHash(VirtualFrame frame, Node inliningTarget, Object object,
Object klass = getClassNode.execute(inliningTarget, object);
TpSlots slots = getSlotsNode.execute(inliningTarget, klass);
if (slots.tp_hash() == null) {
- slots = handleNoHash(frame, inliningTarget, object, readTypeObjectFieldNode,
- typeIsNotReadyProfile, boundaryCallData, raiseNode, klass, slots);
+ slots = handleNoHash(frame, inliningTarget, object, typeIsNotReadyProfile, boundaryCallData, raiseNode, klass, slots);
}
return callHashFun.execute(frame, inliningTarget, slots.tp_hash(), object);
}
@InliningCutoff
- private static TpSlots handleNoHash(VirtualFrame frame, Node inliningTarget, Object object, ReadI64Node readTypeObjectFieldNode,
- InlinedConditionProfile typeIsNotReadyProfile,
+ private static TpSlots handleNoHash(VirtualFrame frame, Node inliningTarget, Object object, InlinedConditionProfile typeIsNotReadyProfile,
BoundaryCallData boundaryCallData, PRaiseNode raiseNode, Object klass, TpSlots slots) {
boolean initialized = false;
if (klass instanceof PythonAbstractNativeObject nativeKlass) {
@@ -197,7 +199,7 @@ private static TpSlots handleNoHash(VirtualFrame frame, Node inliningTarget, Obj
* work without an explicit call to PyType_Ready, we implicitly call PyType_Ready here
* and then check the tp_hash slot again
*/
- long flags = readTypeObjectFieldNode.readFromObj(nativeKlass, CFields.PyTypeObject__tp_flags);
+ long flags = readLongField(nativeKlass.getPtr(), CFields.PyTypeObject__tp_flags);
if (typeIsNotReadyProfile.profile(inliningTarget, (flags & TypeFlags.READY) == 0)) {
Object savedState = BoundaryCallContext.enter(frame, boundaryCallData);
try {
@@ -216,7 +218,12 @@ private static TpSlots handleNoHash(VirtualFrame frame, Node inliningTarget, Obj
@TruffleBoundary
private static TpSlots callTypeReady(Node inliningTarget, Object object, PythonAbstractNativeObject klass) {
- int res = (int) PCallCapiFunction.getUncached().call(NativeCAPISymbol.FUN_PY_TYPE_READY, PythonToNativeNode.executeUncached(klass));
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(klass);
+ PythonContext context = PythonContext.get(null);
+ var callable = CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_PY_TYPE_READY);
+ int res = ExternalFunctionInvoker.invokePY_TYPE_READY(null, C_API_TIMING, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(), context.getThreadState(context.getLanguage(inliningTarget)), callable,
+ PythonToNativeNode.executeLongUncached(klass));
if (res < 0) {
throw raiseSystemError(inliningTarget, klass);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceConcatNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceConcatNode.java
index 4d59b7eebe..f4c2d2eba1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceConcatNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceConcatNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -59,6 +59,7 @@
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
@@ -67,6 +68,7 @@
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
+import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
@@ -75,9 +77,15 @@
@GenerateInline
@GenerateCached(false)
+@GenerateUncached
public abstract class PySequenceConcatNode extends PNodeWithContext {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object v, Object w);
+ @TruffleBoundary
+ public static Object executeUncached(Object v, Object w) {
+ return PySequenceConcatNodeGen.getUncached().execute(null, null, v, w);
+ }
+
@Specialization(guards = {"isBuiltinList(left)", "isBuiltinList(right)"})
static PList doPList(Node inliningTarget, PList left, PList right,
@Shared @Cached SequenceStorageNodes.ConcatListOrTupleNode concatNode,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceContainsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceContainsNode.java
index 7fd3e1120a..ee167f3efa 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceContainsNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceContainsNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2026, 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
@@ -45,6 +45,7 @@
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSqContains.CallSlotSqContainsNode;
import com.oracle.graal.python.lib.PySequenceIterSearchNode.LazyPySequenceIterSeachNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
@@ -64,6 +65,11 @@
public abstract class PySequenceContainsNode extends Node {
public abstract boolean execute(Frame frame, Node inliningTarget, Object container, Object key);
+ @TruffleBoundary
+ public static boolean executeUncached(Object container, Object key) {
+ return PySequenceContainsNodeGen.getUncached().execute(null, null, container, key);
+ }
+
@Specialization
static boolean contains(VirtualFrame frame, Node inliningTarget, Object container, Object key,
@Cached GetClassNode getClassNode,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceDelItemNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceDelItemNode.java
index 78d9a7badb..9d2d50be51 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceDelItemNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceDelItemNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -52,6 +52,7 @@
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
@@ -75,6 +76,11 @@
public abstract class PySequenceDelItemNode extends Node {
public abstract void execute(Frame frame, Node inliningTarget, Object container, int index);
+ @TruffleBoundary
+ public static void executeUncached(Object container, int index) {
+ PySequenceDelItemNodeGen.getUncached().execute(null, null, container, index);
+ }
+
@Specialization(guards = "isBuiltinList(object)", excludeForUncached = true)
static void doList(VirtualFrame frame, PList object, int key,
@Cached(inline = false) ListBuiltins.SetItemNode setItemNode) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceGetItemNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceGetItemNode.java
index 301a481e5f..2b85fb39e9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceGetItemNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceGetItemNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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
@@ -52,6 +52,7 @@
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
@@ -82,6 +83,11 @@ public final Object execute(Object object, int index) {
return execute(null, object, index);
}
+ @TruffleBoundary
+ public static Object executeUncached(Object object, int index) {
+ return PySequenceGetItemNodeGen.getUncached().execute(null, object, index);
+ }
+
@Specialization
static Object doGeneric(VirtualFrame frame, Object object, int index,
@Bind Node inliningTarget,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceInPlaceConcatNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceInPlaceConcatNode.java
index e1b4970142..9e40760d0f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceInPlaceConcatNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceInPlaceConcatNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -52,10 +52,12 @@
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
+import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
@@ -63,9 +65,15 @@
@GenerateInline
@GenerateCached(false)
+@GenerateUncached
public abstract class PySequenceInPlaceConcatNode extends PNodeWithContext {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object v, Object w);
+ @TruffleBoundary
+ public static Object executeUncached(Object v, Object w) {
+ return PySequenceInPlaceConcatNodeGen.getUncached().execute(null, null, v, w);
+ }
+
@Specialization
static Object doIt(VirtualFrame frame, Node inliningTarget, Object v, Object w,
@Cached GetClassNode getVClass,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceInPlaceRepeatNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceInPlaceRepeatNode.java
index e33fe8544a..65f5de8dfd 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceInPlaceRepeatNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceInPlaceRepeatNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -52,10 +52,12 @@
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
+import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
@@ -63,9 +65,15 @@
@GenerateInline
@GenerateCached(false)
+@GenerateUncached
public abstract class PySequenceInPlaceRepeatNode extends PNodeWithContext {
public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object o, int count);
+ @TruffleBoundary
+ public static Object executeUncached(Object o, int count) {
+ return PySequenceInPlaceRepeatNodeGen.getUncached().execute(null, null, o, count);
+ }
+
@Specialization
static Object doIt(VirtualFrame frame, Node inliningTarget, Object o, int count,
@Cached GetClassNode getClassNode,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceIterSearchNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceIterSearchNode.java
index a41b191157..4b4d339f74 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceIterSearchNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceIterSearchNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2026, 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
@@ -47,6 +47,7 @@
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
@@ -81,6 +82,11 @@ public final int execute(Node inliningTarget, Object container, Object key, int
public abstract int execute(Frame frame, Node inliningTarget, Object container, Object key, int operation);
+ @TruffleBoundary
+ public static int executeUncached(Object container, Object key, int operation) {
+ return PySequenceIterSearchNodeGen.getUncached().execute(null, null, container, key, operation);
+ }
+
@Specialization
static int search(Frame frame, Node inliningTarget, Object container, Object key, int operation,
@Cached PyObjectGetIter getIter,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceSetItemNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceSetItemNode.java
index 847387b129..17e5872160 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceSetItemNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceSetItemNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -51,6 +51,7 @@
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
@@ -74,6 +75,11 @@
public abstract class PySequenceSetItemNode extends Node {
public abstract void execute(Frame frame, Node inliningTarget, Object container, int index, Object item);
+ @TruffleBoundary
+ public static void executeUncached(Object container, int index, Object item) {
+ PySequenceSetItemNodeGen.getUncached().execute(null, null, container, index, item);
+ }
+
@Specialization(guards = "isBuiltinList(object)", excludeForUncached = true)
static void doList(VirtualFrame frame, PList object, int key, Object value,
@Cached(inline = false) ListBuiltins.SetItemNode setItemNode) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceSizeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceSizeNode.java
index 7501c0f40a..7957aa6a48 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceSizeNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySequenceSizeNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2026, 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
@@ -57,6 +57,7 @@
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.runtime.exception.PException;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
@@ -86,6 +87,11 @@ public abstract class PySequenceSizeNode extends Node {
public abstract int execute(Frame frame, Node inliningTarget, Object object);
+ @TruffleBoundary
+ public static int executeUncached(Object object) {
+ return PySequenceSizeNodeGen.getUncached().execute(null, null, object);
+ }
+
@Specialization
static int doTruffleString(TruffleString str,
@Cached TruffleString.CodePointLengthNode codePointLengthNode) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySliceNew.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySliceNew.java
index 516c0b2c29..5b818ae76c 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySliceNew.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PySliceNew.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2026, 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
@@ -46,6 +46,7 @@
import com.oracle.graal.python.builtins.objects.slice.PSlice;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.runtime.object.PFactory;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateCached;
@@ -65,6 +66,11 @@
public abstract class PySliceNew extends PNodeWithContext {
public abstract PSlice execute(Node inliningTarget, Object start, Object stop, Object step);
+ @TruffleBoundary
+ public static PSlice executeUncached(Object start, Object stop, Object step) {
+ return PySliceNewNodeGen.getUncached().execute(null, start, stop, step);
+ }
+
@SuppressWarnings("unused")
static PSlice doInt(int start, int stop, PNone step,
@Bind PythonLanguage language) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleSizeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleSizeNode.java
index 20d2ba6e54..31b1997cb4 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleSizeNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleSizeNode.java
@@ -42,11 +42,11 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyVarObject__ob_size;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
import static com.oracle.graal.python.nodes.ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC_S;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
@@ -82,9 +82,8 @@ static int size(PTuple tuple) {
@InliningCutoff
static int sizeNative(Node inliningTarget, PythonAbstractNativeObject tuple,
@SuppressWarnings("unused") @Cached GetClassNode getClassNode,
- @SuppressWarnings("unused") @Cached(inline = false) IsSubtypeNode isSubtypeNode,
- @Cached(inline = false) CStructAccess.ReadI64Node getSize) {
- return PythonUtils.toIntError(getSize.readFromObj(tuple, PyVarObject__ob_size));
+ @SuppressWarnings("unused") @Cached(inline = false) IsSubtypeNode isSubtypeNode) {
+ return PythonUtils.toIntError(readLongField(tuple.getPtr(), PyVarObject__ob_size));
}
@SuppressWarnings("unused")
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
index c449241b4f..72ce90563f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java
@@ -625,7 +625,7 @@ public abstract class ErrorMessages {
public static final TruffleString REQUIRES_STR_OBJECT_BUT_RECEIVED_P = tsLiteral("'%s' requires a 'str' object but received a '%p'");
public static final TruffleString REQUIRED_FIELD_S_MISSING_FROM_S = tsLiteral("required field \"%s\" missing from %s");
public static final TruffleString S_RETURNED_BASE_WITH_UNSUITABLE_LAYOUT = tsLiteral("%s returned base with unsuitable layout ('%p')");
- public static final TruffleString RETURNED_NON_FLOAT = tsLiteral("%p.%s returned non-float (type %p)");
+ public static final TruffleString RETURNED_NON_FLOAT = tsLiteral("%p.__float__() returned non-float (type %p)");
public static final TruffleString RETURNED_NON_INT = tsLiteral("%s returned non-int (type %p)");
public static final TruffleString S_RETURNED_NON_CLASS = tsLiteral("%s returned a non-class ('%p')");
public static final TruffleString RETURNED_NON_INTEGER = tsLiteral("%s returned a non-integer");
@@ -1588,4 +1588,5 @@ public abstract class ErrorMessages {
public static final TruffleString WEAKREF_PROXY_REFERENCED_A_NON_ITERATOR_S_OBJECT = tsLiteral("Weakref proxy referenced a non-iterator '%s' object");
public static final TruffleString S_MUST_BE_CALLABLE = tsLiteral("%s must be callable");
+ public static final TruffleString METHOD_CANNOT_BE_BOTH_CLASS_AND_STATIC = tsLiteral("method cannot be both class and static");
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/HiddenAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/HiddenAttr.java
index f8e1b4fdc3..a5978ccf94 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/HiddenAttr.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/HiddenAttr.java
@@ -54,8 +54,12 @@
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.object.PythonObject;
+import com.oracle.graal.python.nodes.HiddenAttrFactory.ReadLongNodeGen;
import com.oracle.graal.python.nodes.HiddenAttrFactory.ReadNodeGen;
+import com.oracle.graal.python.nodes.HiddenAttrFactory.WriteLongNodeGen;
import com.oracle.graal.python.nodes.HiddenAttrFactory.WriteNodeGen;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateCached;
@@ -65,6 +69,7 @@
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.HiddenKey;
@@ -117,6 +122,12 @@ public String getName() {
return key.getName();
}
+ boolean hasLongValue() {
+ return this == ALLOC || this == AS_BUFFER || this == CLEAR || this == DEALLOC ||
+ this == DEL || this == FREE || this == IS_GC || this == TRAVERSE ||
+ this == METHOD_DEF_PTR;
+ }
+
@Override
public String toString() {
return getName();
@@ -139,6 +150,7 @@ public static Object executeUncached(PythonAbstractObject self, HiddenAttr attr,
@Specialization
static Object doGeneric(PythonAbstractObject self, HiddenAttr attr, Object defaultValue,
@Cached DynamicObject.GetNode getNode) {
+ assert !attr.hasLongValue();
return getNode.execute(self, attr.key, defaultValue);
}
@@ -172,6 +184,7 @@ public static void executeUncached(PythonAbstractObject self, HiddenAttr attr, O
static void doPythonObjectDict(PythonObject self, HiddenAttr attr, Object value,
@Cached DynamicObject.SetShapeFlagsNode setShapeFlagsNode,
@Shared @Cached DynamicObject.PutNode putNode) {
+ assert !attr.hasLongValue();
setShapeFlagsNode.executeAdd(self, HAS_DICT);
if (isGenericDict(self, value)) {
setShapeFlagsNode.executeAdd(self, HAS_MATERIALIZED_DICT);
@@ -189,6 +202,7 @@ private static boolean isGenericDict(PythonObject self, Object value) {
@Specialization(guards = "attr != DICT || !isPythonObject(self)")
static void doGeneric(PythonAbstractObject self, HiddenAttr attr, Object value,
@Shared @Cached DynamicObject.PutNode putNode) {
+ assert !attr.hasLongValue();
putNode.execute(self, attr.key, value);
}
@@ -206,4 +220,74 @@ public static WriteNode getUncached() {
return WriteNodeGen.getUncached();
}
}
+
+ @GenerateInline(inlineByDefault = true)
+ @GenerateCached
+ @GenerateUncached
+ public abstract static class ReadLongNode extends Node {
+ public abstract long execute(Node inliningTarget, PythonAbstractObject self, HiddenAttr attr, long defaultValue);
+
+ public final long executeCached(PythonAbstractObject self, HiddenAttr attr, long defaultValue) {
+ return execute(this, self, attr, defaultValue);
+ }
+
+ public static long executeUncached(PythonAbstractObject self, HiddenAttr attr, long defaultValue) {
+ return ReadLongNodeGen.getUncached().execute(null, self, attr, defaultValue);
+ }
+
+ @Specialization
+ static long doGeneric(PythonAbstractObject self, HiddenAttr attr, long defaultValue,
+ @Cached DynamicObject.GetNode getNode) {
+ assert attr.hasLongValue();
+ try {
+ return getNode.executeLong(self, attr.key, defaultValue);
+ } catch (UnexpectedResultException e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ }
+
+ @NeverDefault
+ public static ReadLongNode create() {
+ return ReadLongNodeGen.create();
+ }
+
+ @NeverDefault
+ public static ReadLongNode getUncached() {
+ return ReadLongNodeGen.getUncached();
+ }
+ }
+
+ @GenerateInline(inlineByDefault = true)
+ @GenerateCached
+ @GenerateUncached
+ @ImportStatic(HiddenAttr.class)
+ public abstract static class WriteLongNode extends Node {
+ public abstract void execute(Node inliningTarget, PythonAbstractObject self, HiddenAttr attr, long value);
+
+ public final void executeCached(PythonAbstractObject self, HiddenAttr attr, long value) {
+ execute(this, self, attr, value);
+ }
+
+ @TruffleBoundary
+ public static void executeUncached(PythonAbstractObject self, HiddenAttr attr, long value) {
+ WriteLongNodeGen.getUncached().execute(null, self, attr, value);
+ }
+
+ @Specialization
+ static void doGeneric(PythonAbstractObject self, HiddenAttr attr, long value,
+ @Cached DynamicObject.PutNode putNode) {
+ assert attr.hasLongValue();
+ putNode.execute(self, attr.key, value);
+ }
+
+ @NeverDefault
+ public static WriteLongNode create() {
+ return WriteLongNodeGen.create();
+ }
+
+ @NeverDefault
+ public static WriteLongNode getUncached() {
+ return WriteLongNodeGen.getUncached();
+ }
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java
index 55a41a4b80..fc55f5f0bd 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java
@@ -52,7 +52,6 @@
import com.oracle.graal.python.builtins.objects.bytes.PBytesLike;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper;
import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
import com.oracle.graal.python.builtins.objects.code.PCode;
import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
@@ -524,12 +523,6 @@ public static boolean isIndexOrSlice(Node inliningTarget, PyIndexCheckNode index
return indexCheckNode.execute(inliningTarget, key) || isPSlice(key);
}
- @InliningCutoff
- public static boolean isNativeWrapper(PythonAbstractObject object) {
- PythonNativeWrapper wrapper = object.getNativeWrapper();
- return wrapper != null && wrapper.isNative();
- }
-
public static boolean isNullOrZero(Object value, InteropLibrary lib) {
if (value instanceof Long) {
return ((long) value) == 0;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/CreateArgumentsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/CreateArgumentsNode.java
index e2298e39a3..e4f633836a 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/CreateArgumentsNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/CreateArgumentsNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -122,6 +122,12 @@ public abstract class CreateArgumentsNode extends PNodeWithContext {
public abstract Object[] execute(Node inliningTarget, Object callableOrName, Object[] userArguments, PKeyword[] keywords, Signature signature, Object self, Object classObject,
Object[] defaults, PKeyword[] kwdefaults, boolean methodcall);
+ @TruffleBoundary
+ public static Object[] executeUncached(Object callableOrName, Object[] userArguments, PKeyword[] keywords, Signature signature, Object self, Object classObject,
+ Object[] defaults, PKeyword[] kwdefaults, boolean methodcall) {
+ return CreateArgumentsNodeGen.getUncached().execute(null, callableOrName, userArguments, keywords, signature, self, classObject, defaults, kwdefaults, methodcall);
+ }
+
@Specialization
static Object[] doIt(Node inliningTarget, Object callableOrName, Object[] userArguments, PKeyword[] keywords, Signature signature, Object self, Object classObject, Object[] defaults,
PKeyword[] kwdefaults, boolean methodcall,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/keywords/ExpandKeywordStarargsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/keywords/ExpandKeywordStarargsNode.java
index 76e6d8b836..47cdbea35e 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/keywords/ExpandKeywordStarargsNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/keywords/ExpandKeywordStarargsNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -46,6 +46,7 @@
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateUncached;
@@ -63,6 +64,11 @@ public final PKeyword[] executeCached(Object starargs) {
return execute(null, this, starargs);
}
+ @TruffleBoundary
+ public static PKeyword[] executeUncached(Object starargs) {
+ return getUncached().execute(null, null, starargs);
+ }
+
public final PKeyword[] execute(Node inliningTarget, Object starargs) {
return execute(null, inliningTarget, starargs);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/positional/ExecutePositionalStarargsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/positional/ExecutePositionalStarargsNode.java
index 53b0acff90..88b789d1c7 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/positional/ExecutePositionalStarargsNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/positional/ExecutePositionalStarargsNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -60,6 +60,7 @@
import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.graal.python.runtime.exception.PythonErrorType;
import com.oracle.graal.python.util.ArrayBuilder;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
@@ -79,6 +80,11 @@
public abstract class ExecutePositionalStarargsNode extends Node {
public abstract Object[] executeWith(Frame frame, Object starargs);
+ @TruffleBoundary
+ public static Object[] executeUncached(Object starargs) {
+ return getUncached().executeWith(null, starargs);
+ }
+
@Specialization
static Object[] doObjectArray(Object[] starargs) {
return starargs;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowArray.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowArray.java
index 5d2b56f03f..f3837a6f4f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowArray.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowArray.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -40,11 +40,13 @@
*/
package com.oracle.graal.python.nodes.arrow;
-import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.POINTER_SIZE;
+
import com.oracle.graal.python.util.PythonUtils;
-import sun.misc.Unsafe;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleString.Encoding;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.POINTER_SIZE;
+import sun.misc.Unsafe;
/**
* C Data Interface ArrowArray.
@@ -73,7 +75,11 @@
public class ArrowArray {
private static final Unsafe unsafe = PythonUtils.initUnsafe();
- public static final byte[] CAPSULE_NAME = PyCapsule.capsuleName("arrow_array");
+ /**
+ * This name is used for a PyCapsule which requires a {@code const char *} as name. We therefore
+ * use encoding UTF8. The TruffleString is later transformed to a native string.
+ */
+ public static final TruffleString CAPSULE_NAME = TruffleString.fromConstant("arrow_array", Encoding.UTF_8);
public static final byte NULL = 0;
private static final byte SIZE_OF = 80;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowSchema.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowSchema.java
index 0ac28db944..0430f10807 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowSchema.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/ArrowSchema.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -40,11 +40,13 @@
*/
package com.oracle.graal.python.nodes.arrow;
-import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.POINTER_SIZE;
+
import com.oracle.graal.python.util.PythonUtils;
-import sun.misc.Unsafe;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleString.Encoding;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.POINTER_SIZE;
+import sun.misc.Unsafe;
/**
* C Data Interface of ArrowSchema
@@ -73,7 +75,11 @@ public class ArrowSchema {
private static final Unsafe unsafe = PythonUtils.initUnsafe();
private static final int SIZE_OF = 72;
- public static final byte[] CAPSULE_NAME = PyCapsule.capsuleName("arrow_schema");
+ /**
+ * This name is used for a PyCapsule which requires a {@code const char *} as name. We therefore
+ * use encoding UTF8. The TruffleString is later transformed to a native string.
+ */
+ public static final TruffleString CAPSULE_NAME = TruffleString.fromConstant("arrow_schema", Encoding.UTF_8);
private static final byte NULL = 0;
private static final long FORMAT_INDEX = 0;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/InvokeArrowReleaseCallbackNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/InvokeArrowReleaseCallbackNode.java
index d7986b975d..fd1a0f1c8f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/InvokeArrowReleaseCallbackNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/InvokeArrowReleaseCallbackNode.java
@@ -73,7 +73,7 @@ static void doIt(Node inliningTarget, long releaseCallback, long baseStructure,
@Cached(value = "createReleaseCallbackSignature($node, ctx)", allowUncached = true) Object callbackSignature,
@CachedLibrary(limit = "1") SignatureLibrary signatureLibrary) {
try {
- signatureLibrary.call(callbackSignature, new NativePointer(releaseCallback), baseStructure);
+ signatureLibrary.call(callbackSignature, NativePointer.wrap(releaseCallback), baseStructure);
} catch (Exception e) {
throw CompilerDirectives.shouldNotReachHere("Unable to call release callback. Error:", e);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowArrayCapsuleDestructor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowArrayCapsuleDestructor.java
index ec481d2cab..3963081413 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowArrayCapsuleDestructor.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowArrayCapsuleDestructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -43,21 +43,22 @@
import com.oracle.graal.python.builtins.modules.cext.PythonCextCapsuleBuiltins.PyCapsuleGetPointerNode;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.arrow.ArrowArray;
import com.oracle.graal.python.nodes.arrow.InvokeArrowReleaseCallbackNode;
import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleString.Encoding;
@ExportLibrary(InteropLibrary.class)
public class ArrowArrayCapsuleDestructor implements TruffleObject {
@@ -68,41 +69,37 @@ boolean isExecutable() {
}
@ExportMessage
- static class Execute {
-
- @Specialization(guards = "isPointer(args, interopLib)")
- static Object doRelease(ArrowArrayCapsuleDestructor self, Object[] args,
- @Bind Node inliningTarget,
- @CachedLibrary(limit = "1") InteropLibrary interopLib,
- @Cached NativeToPythonNode nativeToPythonNode,
- @Cached PyCapsuleGetPointerNode capsuleGetPointerNode,
- @Cached InvokeArrowReleaseCallbackNode.Lazy invokeReleaseCallbackNode) {
- Object capsule = nativeToPythonNode.execute(args[0]);
- var capsuleName = new CArrayWrappers.CByteArrayWrapper(ArrowArray.CAPSULE_NAME);
- var arrowArray = ArrowArray.wrap((long) capsuleGetPointerNode.execute(inliningTarget, capsule, capsuleName));
- /*
- * The exported PyCapsules should have a destructor that calls the release callback of
- * the Arrow struct, if it is not already null. This prevents a memory leak in case the
- * capsule was never passed to another consumer.
- *
- * For more information see:
- * https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html#lifetime-
- * semantics
- */
- if (!arrowArray.isReleased()) {
- invokeReleaseCallbackNode.get(inliningTarget).executeCached(arrowArray.releaseCallback(), arrowArray.memoryAddress());
- }
- PythonContext.get(inliningTarget).getUnsafe().freeMemory(arrowArray.memoryAddress());
- return PNone.NO_VALUE;
- }
-
- @Fallback
- static Object doError(ArrowArrayCapsuleDestructor self, Object[] args) {
+ Object execute(Object[] args,
+ @Bind Node inliningTarget,
+ @CachedLibrary(limit = "1") InteropLibrary lib,
+ @Cached NativeToPythonNode nativeToPythonNode,
+ @Cached PyCapsuleGetPointerNode capsuleGetPointerNode,
+ @Cached InvokeArrowReleaseCallbackNode.Lazy invokeReleaseCallbackNode,
+ @Cached TruffleString.AsNativeNode asNativeNode,
+ @Cached TruffleString.GetInternalNativePointerNode getInternalNativePointerNode) {
+ if (args.length != 1 || !lib.isPointer(args[0])) {
throw CompilerDirectives.shouldNotReachHere();
}
- static boolean isPointer(Object[] args, InteropLibrary interopLib) {
- return args.length == 1 && interopLib.isPointer(args[0]);
+ Object capsule = nativeToPythonNode.execute(args[0]);
+ PythonContext ctx = PythonContext.get(inliningTarget);
+ ctx.ensureNativeAccess();
+ TruffleString capsuleName = asNativeNode.execute(ArrowArray.CAPSULE_NAME, ctx::allocateContextMemory, Encoding.UTF_8, false, true);
+ long capsuleNamePointer = PythonUtils.coerceToLong(getInternalNativePointerNode.execute(capsuleName, Encoding.UTF_8), lib);
+ var arrowArray = ArrowArray.wrap(capsuleGetPointerNode.execute(inliningTarget, capsule, capsuleNamePointer));
+ /*
+ * The exported PyCapsules should have a destructor that calls the release callback of the
+ * Arrow struct, if it is not already null. This prevents a memory leak in case the capsule
+ * was never passed to another consumer.
+ *
+ * For more information see:
+ * https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html#lifetime-
+ * semantics
+ */
+ if (!arrowArray.isReleased()) {
+ invokeReleaseCallbackNode.get(inliningTarget).executeCached(arrowArray.releaseCallback(), arrowArray.memoryAddress());
}
+ NativeMemory.free(arrowArray.memoryAddress());
+ return PNone.NO_VALUE;
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowSchemaCapsuleDestructor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowSchemaCapsuleDestructor.java
index 3ac4d7f061..cc21739bfa 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowSchemaCapsuleDestructor.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/arrow/capsule/ArrowSchemaCapsuleDestructor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -43,21 +43,22 @@
import com.oracle.graal.python.builtins.modules.cext.PythonCextCapsuleBuiltins.PyCapsuleGetPointerNode;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonNode;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.nodes.arrow.ArrowSchema;
import com.oracle.graal.python.nodes.arrow.InvokeArrowReleaseCallbackNode;
import com.oracle.graal.python.runtime.PythonContext;
+import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleString.Encoding;
@ExportLibrary(InteropLibrary.class)
public class ArrowSchemaCapsuleDestructor implements TruffleObject {
@@ -68,34 +69,30 @@ boolean isExecutable() {
}
@ExportMessage
- static class Execute {
-
- @Specialization(guards = "isPointer(args, interopLib)")
- static Object doRelease(ArrowSchemaCapsuleDestructor self, Object[] args,
- @Bind Node inliningTarget,
- @CachedLibrary(limit = "1") InteropLibrary interopLib,
- @Cached NativeToPythonNode nativeToPythonNode,
- @Cached PyCapsuleGetPointerNode pyCapsuleGetPointerNode, @Cached InvokeArrowReleaseCallbackNode.Lazy invokeReleaseCallbackNode) {
- Object capsule = nativeToPythonNode.execute(args[0]);
- var capsuleName = new CArrayWrappers.CByteArrayWrapper(ArrowSchema.CAPSULE_NAME);
- var arrowSchema = ArrowSchema.wrap((long) pyCapsuleGetPointerNode.execute(inliningTarget, capsule, capsuleName));
-
- if (!arrowSchema.isReleased()) {
- invokeReleaseCallbackNode.get(inliningTarget).executeCached(arrowSchema.releaseCallback(), arrowSchema.memoryAddress());
- }
-
- PythonContext.get(inliningTarget).getUnsafe().freeMemory(arrowSchema.memoryAddress());
- return PNone.NO_VALUE;
- }
-
- @Fallback
- static Object doError(ArrowSchemaCapsuleDestructor self, Object[] args) {
+ Object execute(Object[] args,
+ @Bind Node inliningTarget,
+ @CachedLibrary(limit = "1") InteropLibrary lib,
+ @Cached NativeToPythonNode nativeToPythonNode,
+ @Cached PyCapsuleGetPointerNode pyCapsuleGetPointerNode,
+ @Cached InvokeArrowReleaseCallbackNode.Lazy invokeReleaseCallbackNode,
+ @Cached TruffleString.AsNativeNode asNativeNode,
+ @Cached TruffleString.GetInternalNativePointerNode getInternalNativePointerNode) {
+ if (args.length != 1 || !lib.isPointer(args[0])) {
throw CompilerDirectives.shouldNotReachHere();
}
- static boolean isPointer(Object[] args, InteropLibrary interopLib) {
- return args.length == 1 && interopLib.isPointer(args[0]);
+ Object capsule = nativeToPythonNode.execute(args[0]);
+ PythonContext ctx = PythonContext.get(inliningTarget);
+ ctx.ensureNativeAccess();
+ TruffleString capsuleName = asNativeNode.execute(ArrowSchema.CAPSULE_NAME, ctx::allocateContextMemory, Encoding.UTF_8, false, true);
+ long capsuleNamePointer = PythonUtils.coerceToLong(getInternalNativePointerNode.execute(capsuleName, Encoding.UTF_8), lib);
+ var arrowSchema = ArrowSchema.wrap(pyCapsuleGetPointerNode.execute(inliningTarget, capsule, capsuleNamePointer));
+
+ if (!arrowSchema.isReleased()) {
+ invokeReleaseCallbackNode.get(inliningTarget).executeCached(arrowSchema.releaseCallback(), arrowSchema.memoryAddress());
}
- }
+ NativeMemory.free(arrowSchema.memoryAddress());
+ return PNone.NO_VALUE;
+ }
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java
index 7a95566f8d..05053c6696 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java
@@ -41,6 +41,7 @@
package com.oracle.graal.python.nodes.attributes;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_dict;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
import static com.oracle.graal.python.builtins.objects.object.PythonObject.HAS_NO_VALUE_PROPERTIES;
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
@@ -224,9 +225,8 @@ static boolean writeToDict(PDict dict, TruffleString key, Object value,
}
private static void checkNativeImmutable(Node inliningTarget, PythonAbstractNativeObject object, TruffleString key,
- CStructAccess.ReadI64Node getNativeFlags,
PRaiseNode raiseNode) {
- long flags = getNativeFlags.readFromObj(object, CFields.PyTypeObject__tp_flags);
+ long flags = readLongField(object.getPtr(), CFields.PyTypeObject__tp_flags);
if ((flags & TypeFlags.IMMUTABLETYPE) != 0) {
throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, key, object);
}
@@ -237,7 +237,6 @@ static boolean writeNativeObjectOrClass(PythonAbstractNativeObject object, Truff
@Bind Node inliningTarget,
@Cached InlinedConditionProfile isTypeProfile,
@Shared("getDict") @Cached GetDictIfExistsNode getDict,
- @Cached CStructAccess.ReadI64Node getNativeFlags,
@Cached CStructAccess.ReadObjectNode getNativeDict,
@Exclusive @Cached HashingStorageSetItem setHashingStorageItem,
@Exclusive @Cached InlinedBranchProfile updateStorage,
@@ -247,7 +246,7 @@ static boolean writeNativeObjectOrClass(PythonAbstractNativeObject object, Truff
try {
Object dict;
if (isType) {
- checkNativeImmutable(inliningTarget, object, key, getNativeFlags, raiseNode);
+ checkNativeImmutable(inliningTarget, object, key, raiseNode);
/*
* For native types, the type attributes are stored in a dict that is located in
* 'typePtr->tp_dict'. So, this is different to a native object (that is not a type)
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToPythonObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToPythonObjectNode.java
index 1982b5896c..68ce0ac525 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToPythonObjectNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToPythonObjectNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2026, 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
@@ -43,6 +43,7 @@
import com.oracle.graal.python.builtins.objects.object.PythonObject;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.runtime.PythonOptions;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateUncached;
@@ -55,8 +56,9 @@
/**
* Writes attribute directly to the underlying {@link DynamicObject} regardless of whether the
* object has dict, also bypasses any other additional logic in {@link WriteAttributeToObjectNode}.
- * This node does not provide any functionality on top of {@link DynamicObject.PutNode}, its purpose
- * is to provide an abstraction in preparation for the transition from {@link DynamicObject} to
+ * This node does not provide any functionality on top of
+ * {@link com.oracle.truffle.api.object.DynamicObject.PutNode}, its purpose is to provide an
+ * abstraction in preparation for the transition from {@link DynamicObject} to
* {@link com.oracle.graal.python.builtins.objects.common.ObjectHashMap}.
*/
@ImportStatic(PythonOptions.class)
@@ -66,6 +68,7 @@ public abstract class WriteAttributeToPythonObjectNode extends PNodeWithContext
public abstract void execute(PythonObject primary, TruffleString key, Object value);
+ @TruffleBoundary
public static void executeUncached(PythonObject primary, TruffleString key, Object value) {
WriteAttributeToPythonObjectNodeGen.getUncached().execute(primary, key, value);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java
index 941e5bff63..914d3e2ccb 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/ListNodes.java
@@ -43,6 +43,8 @@
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyListObject__allocated;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyListObject__ob_item;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyVarObject__ob_size;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
import static com.oracle.graal.python.nodes.ErrorMessages.DESCRIPTOR_REQUIRES_S_OBJ_RECEIVED_P;
import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
@@ -50,7 +52,6 @@
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ListGeneralizationNode;
import com.oracle.graal.python.builtins.objects.ints.PInt;
@@ -339,13 +340,12 @@ public abstract static class GetNativeListStorage extends Node {
public abstract NativeSequenceStorage execute(PythonAbstractNativeObject list);
@Specialization
- NativeSequenceStorage getNative(PythonAbstractNativeObject list,
- @Cached CStructAccess.ReadPointerNode getContents,
- @Cached CStructAccess.ReadI64Node readI64Node) {
+ NativeSequenceStorage getNative(PythonAbstractNativeObject list) {
assert IsSubtypeNode.getUncached().execute(GetClassNode.executeUncached(list), PythonBuiltinClassType.PList);
- Object array = getContents.readFromObj(list, PyListObject__ob_item);
- int size = (int) readI64Node.readFromObj(list, PyVarObject__ob_size);
- int allocated = (int) readI64Node.readFromObj(list, PyListObject__allocated);
+ long listRawPtr = list.getPtr();
+ long array = readPtrField(listRawPtr, PyListObject__ob_item);
+ int size = (int) readLongField(listRawPtr, PyVarObject__ob_size);
+ int allocated = (int) readLongField(listRawPtr, PyListObject__allocated);
return NativeObjectSequenceStorage.create(array, size, allocated, false);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java
index 2bf71294a5..064a87f9a9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/builtins/TupleNodes.java
@@ -40,12 +40,13 @@
*/
package com.oracle.graal.python.nodes.builtins;
-import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTupleObject__ob_item;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyVarObject__ob_size;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
+import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.CreateStorageFromIteratorNode;
@@ -144,12 +145,11 @@ public abstract static class GetNativeTupleStorage extends Node {
public abstract NativeObjectSequenceStorage execute(PythonAbstractNativeObject tuple);
@Specialization
- NativeObjectSequenceStorage getNative(PythonAbstractNativeObject tuple,
- @Cached CStructAccess.ReadPointerNode getContents,
- @Cached CStructAccess.ReadI64Node readI64Node) {
+ NativeObjectSequenceStorage getNative(PythonAbstractNativeObject tuple) {
assert PyTupleCheckNode.executeUncached(tuple);
- Object array = getContents.readFromObj(tuple, PyTupleObject__ob_item);
- int size = (int) readI64Node.readFromObj(tuple, PyVarObject__ob_size);
+ long tupleRawPtr = tuple.getPtr();
+ long array = CStructAccess.getFieldPtr(tupleRawPtr, CFields.PyTupleObject__ob_item);
+ int size = (int) readLongField(tupleRawPtr, PyVarObject__ob_size);
return NativeObjectSequenceStorage.create(array, size, size, false);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/GetCurrentFrameRef.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/GetCurrentFrameRef.java
index 6035662969..1c7512e99a 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/GetCurrentFrameRef.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/GetCurrentFrameRef.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2026, 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
@@ -47,6 +47,7 @@
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
@@ -69,6 +70,11 @@ public abstract class GetCurrentFrameRef extends Node {
public abstract Reference execute(Frame frame, Node inliningTarget);
+ @TruffleBoundary
+ public static Reference executeUncached() {
+ return GetCurrentFrameRefNodeGen.getUncached().execute(null, null);
+ }
+
@Specialization(guards = "frame != null")
static Reference doWithFrame(Frame frame) {
return PArguments.getCurrentFrameInfo(frame);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadFrameNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadFrameNode.java
index 93ef281d50..bc476fc818 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadFrameNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadFrameNode.java
@@ -139,6 +139,10 @@ public static ReadFrameNode create() {
return ReadFrameNodeGen.create();
}
+ public static ReadFrameNode getUncached() {
+ return ReadFrameNodeGen.getUncached();
+ }
+
/**
* Get the current python-level frame (skips builtin function roots, but not internal python
* frames)
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java
index 67f37f8f5d..f241181c12 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java
@@ -46,7 +46,6 @@
import com.oracle.graal.python.builtins.objects.PythonAbstractObject;
import com.oracle.graal.python.builtins.objects.cell.PCell;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis;
import com.oracle.graal.python.builtins.objects.object.PythonObject;
@@ -194,11 +193,6 @@ static Object getCell(@SuppressWarnings("unused") PCell object) {
return PythonBuiltinClassType.PCell;
}
- @Specialization
- static Object getNativeVoidPtr(@SuppressWarnings("unused") PythonNativeVoidPtr object) {
- return PythonBuiltinClassType.PInt;
- }
-
@InliningCutoff
@Specialization(guards = "isForeignObject(object)")
static Object getForeign(Object object,
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java
index 6f60853cb1..195eec107d 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java
@@ -43,22 +43,31 @@
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_OBJECT_GET_DICT_PTR;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_dict;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+
+import java.lang.ref.Reference;
import com.oracle.graal.python.PythonLanguage;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
+import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.ReadObjectNode;
+import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.WriteObjectNewRefNode;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.module.PythonModule;
import com.oracle.graal.python.builtins.objects.object.PythonObject;
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode;
+import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.HiddenAttr;
+import com.oracle.graal.python.nodes.HiddenAttr.ReadNode;
import com.oracle.graal.python.nodes.PNodeWithContext;
import com.oracle.graal.python.nodes.PRaiseNode;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
@@ -70,8 +79,6 @@
import com.oracle.truffle.api.dsl.Idempotent;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
@@ -118,14 +125,14 @@ protected boolean dictIsConstant(PythonObject object) {
}
public static PDict getDictUncached(PythonObject object) {
- return (PDict) HiddenAttr.ReadNode.executeUncached(object, HiddenAttr.DICT, null);
+ return (PDict) ReadNode.executeUncached(object, HiddenAttr.DICT, null);
}
@Specialization(replaces = "getConstant")
@InliningCutoff
static PDict doPythonObject(PythonObject object,
@Bind Node inliningTarget,
- @Cached HiddenAttr.ReadNode readHiddenAttrNode) {
+ @Cached ReadNode readHiddenAttrNode) {
return (PDict) readHiddenAttrNode.execute(inliningTarget, object, HiddenAttr.DICT, null);
}
@@ -134,13 +141,12 @@ static PDict doPythonObject(PythonObject object,
static PDict doNativeObject(PythonAbstractNativeObject object,
@Bind Node inliningTarget,
@Cached IsTypeNode isTypeNode,
- @Cached CStructAccess.ReadObjectNode getNativeDict,
- @CachedLibrary(limit = "1") InteropLibrary lib,
- @Cached PythonToNativeNode toNative,
- @Cached CStructAccess.ReadObjectNode readObjectNode,
- @Cached CStructAccess.WriteObjectNewRefNode writeObjectNode,
+ @Cached ReadObjectNode getNativeDict,
+ @Cached ReadObjectNode readObjectNode,
+ @Cached WriteObjectNewRefNode writeObjectNode,
@Cached InlinedBranchProfile createDict,
- @Cached CExtNodes.PCallCapiFunction callGetDictPtr) {
+ @Cached PythonToNativeInternalNode pythonToNativeNode,
+ @Cached("createFor($node)") BoundaryCallData boundaryCallData) {
if (isTypeNode.execute(inliningTarget, object)) {
// Optimization for native types: read at the known offset instead of calling
// _PyObject_GetDictPtr()
@@ -152,22 +158,29 @@ static PDict doNativeObject(PythonAbstractNativeObject object,
}
}
- Object dictPtr = callGetDictPtr.call(FUN_PY_OBJECT_GET_DICT_PTR, toNative.execute(object));
- if (lib.isNull(dictPtr)) {
- return null;
- } else {
- Object dictObject = readObjectNode.readGeneric(dictPtr, 0);
- if (dictObject == PNone.NO_VALUE) {
- createDict.enter(inliningTarget);
- PDict dict = PFactory.createDict(PythonLanguage.get(inliningTarget));
- writeObjectNode.write(dictPtr, dict);
- return dict;
- } else if (dictObject instanceof PDict dict) {
- return dict;
+ assert EnsurePythonObjectNode.doesNotNeedPromotion(object);
+ NativeFunctionPointer callable = CApiContext.getNativeSymbol(inliningTarget, FUN_PY_OBJECT_GET_DICT_PTR);
+ try {
+ long dictPtr = ExternalFunctionInvoker.invokeGETDICTPTRFUN(callable.getAddress(), pythonToNativeNode.execute(inliningTarget, object, false));
+ Reference.reachabilityFence(object);
+ if (dictPtr == NULLPTR) {
+ return null;
} else {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.DICT_MUST_BE_SET_TO_DICT, dictObject);
+ Object dictObject = readObjectNode.read(dictPtr, 0);
+ if (dictObject == PNone.NO_VALUE) {
+ createDict.enter(inliningTarget);
+ PDict dict = PFactory.createDict(PythonLanguage.get(inliningTarget));
+ writeObjectNode.write(dictPtr, dict);
+ return dict;
+ } else if (dictObject instanceof PDict dict) {
+ return dict;
+ } else {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.DICT_MUST_BE_SET_TO_DICT, dictObject);
+ }
}
+ } catch (Throwable t) {
+ throw CompilerDirectives.shouldNotReachHere(t);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/SetDictNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/SetDictNode.java
index c1a06d0489..67bb986913 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/SetDictNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/SetDictNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2026, 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,17 +41,22 @@
package com.oracle.graal.python.nodes.object;
import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_OBJECT_GENERIC_SET_DICT;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+
+import java.lang.ref.Reference;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
+import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckPrimitiveFunctionResultNode;
-import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
import com.oracle.graal.python.builtins.objects.dict.PDict;
import com.oracle.graal.python.builtins.objects.object.PythonObject;
import com.oracle.graal.python.builtins.objects.type.PythonClass;
import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode;
import com.oracle.graal.python.nodes.HiddenAttr;
-import com.oracle.graal.python.nodes.PNodeWithContext;
+import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
@@ -65,7 +70,9 @@
@GenerateUncached
@GenerateInline
@GenerateCached(false)
-public abstract class SetDictNode extends PNodeWithContext {
+public abstract class SetDictNode extends Node {
+ private static final CApiTiming C_API_TIMING = CApiTiming.create(true, FUN_PY_OBJECT_GENERIC_SET_DICT);
+
public abstract void execute(Node inliningTarget, Object object, PDict dict);
public static void executeUncached(Object object, PDict dict) {
@@ -86,15 +93,21 @@ static void doPythonObjectNotClass(Node inliningTarget, PythonObject object, PDi
}
@Specialization
- void doNativeObject(PythonAbstractNativeObject object, PDict dict,
- @Cached(inline = false) PythonToNativeNode objectToSulong,
- @Cached(inline = false) PythonToNativeNode dictToSulong,
- @Cached(inline = false) CExtNodes.PCallCapiFunction callGetDictNode,
- @Cached(inline = false) CheckPrimitiveFunctionResultNode checkResult) {
+ static void doNativeObject(Node inliningTarget, PythonAbstractNativeObject object, PDict dict,
+ @Cached PythonToNativeInternalNode objectToNative,
+ @Cached PythonToNativeInternalNode dictToNative,
+ @Cached CheckPrimitiveFunctionResultNode checkResult) {
assert !IsTypeNode.executeUncached(object);
- PythonContext context = getContext();
- Object result = callGetDictNode.call(FUN_PY_OBJECT_GENERIC_SET_DICT, objectToSulong.execute(object), dictToSulong.execute(dict), context.getNativeNull());
- checkResult.execute(context, FUN_PY_OBJECT_GENERIC_SET_DICT.getTsName(), result);
+ long objectPointer = objectToNative.execute(inliningTarget, object, false);
+ long dictPointer = dictToNative.execute(inliningTarget, dict, false);
+ PythonContext context = PythonContext.get(inliningTarget);
+ var callable = CApiContext.getNativeSymbol(inliningTarget, FUN_PY_OBJECT_GENERIC_SET_DICT);
+ int result = ExternalFunctionInvoker.invokePY_OBJECT_GENERIC_SET_DICT(null, C_API_TIMING, context.ensureNativeContext(),
+ BoundaryCallData.getUncached(),
+ context.getThreadState(context.getLanguage(inliningTarget)), callable, objectPointer, dictPointer, NULLPTR);
+ checkResult.executeLong(inliningTarget, context.getThreadState(context.getLanguage(inliningTarget)), FUN_PY_OBJECT_GENERIC_SET_DICT.getTsName(), result);
+ Reference.reachabilityFence(object);
+ Reference.reachabilityFence(dict);
}
protected static boolean isPythonClass(Object object) {
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaDoubleNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaDoubleNode.java
index 14c2fb06ac..423366e64d 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaDoubleNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaDoubleNode.java
@@ -41,11 +41,11 @@
package com.oracle.graal.python.nodes.util;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyFloatObject__ob_fval;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readDoubleField;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.modules.MathGuards;
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.ints.PInt;
import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PNodeWithContext;
@@ -123,10 +123,9 @@ static double doPBCT(@SuppressWarnings("unused") PythonBuiltinClassType object)
@InliningCutoff
static double doNativeObject(Node inliningTarget, PythonAbstractNativeObject x,
@Cached GetPythonObjectClassNode getClassNode,
- @Cached(inline = false) IsSubtypeNode isSubtypeNode,
- @Cached(inline = false) CStructAccess.ReadDoubleNode read) {
+ @Cached(inline = false) IsSubtypeNode isSubtypeNode) {
if (isSubtypeNode.execute(getClassNode.execute(inliningTarget, x), PythonBuiltinClassType.PFloat)) {
- return read.readFromObj(x, PyFloatObject__ob_fval);
+ return readDoubleField(x.getPtr(), PyFloatObject__ob_fval);
}
// the object's type is not a subclass of 'float'
throw CannotCastException.INSTANCE;
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaIntLossyNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaIntLossyNode.java
index 6c032595ff..741d500a96 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaIntLossyNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaIntLossyNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaLongLossyNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaLongLossyNode.java
index e8be4f251f..e4283c758f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaLongLossyNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToJavaLongLossyNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToTruffleStringNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToTruffleStringNode.java
index 2d97f52b58..1c0ac1c605 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToTruffleStringNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToTruffleStringNode.java
@@ -43,12 +43,14 @@
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyASCIIObject__length;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyASCIIObject__state;
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyUnicodeObject__data;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readIntField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
+import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readPtrField;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.str.PString;
import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node;
import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node;
@@ -134,21 +136,16 @@ static TruffleString doPStringGeneric(Node inliningTarget, PString x,
@GenerateInline(false) // Footprint reduction 48 -> 29
public abstract static class ReadNativeStringNode extends PNodeWithContext {
- public abstract TruffleString execute(Object pointer);
+ public abstract TruffleString execute(long pointer);
@Specialization
- static TruffleString read(Object pointer,
- @Cached CStructAccess.ReadI32Node readI32,
- @Cached CStructAccess.ReadI64Node readI64,
- @Cached CStructAccess.ReadPointerNode readPointer,
- @Cached CStructAccess.ReadByteNode readByte,
- @CachedLibrary(limit = "3") InteropLibrary lib,
+ static TruffleString read(long rawPointer,
@Cached TruffleString.FromNativePointerWithCompactionUTF32Node fromNative,
- @Cached TruffleString.FromByteArrayWithCompactionUTF32Node fromBytes) {
- int state = readI32.read(pointer, PyASCIIObject__state);
+ @Cached TruffleString.SwitchEncodingNode switchEncodingNode) {
+ int state = readIntField(rawPointer, PyASCIIObject__state);
int kind = (state >> CFields.PyASCIIObject__state_kind_shift) & 0x7;
- Object data = readPointer.read(pointer, PyUnicodeObject__data);
- long length = readI64.read(pointer, PyASCIIObject__length);
+ long data = readPtrField(rawPointer, PyUnicodeObject__data);
+ long length = readLongField(rawPointer, PyASCIIObject__length);
TruffleString.CompactionLevel compactionLevel;
if (kind == 1) {
@@ -163,11 +160,8 @@ static TruffleString read(Object pointer,
}
int bytes = PythonUtils.toIntError(length * kind);
- if (lib.isPointer(data) || data instanceof Long) {
- return fromNative.execute(data, 0, bytes, compactionLevel, false);
- }
- byte[] result = readByte.readByteArray(data, bytes);
- return fromBytes.execute(result, 0, result.length, compactionLevel, false);
+ TruffleString ts = fromNative.execute(data, 0, bytes, compactionLevel, true);
+ return switchEncodingNode.execute(ts, TS_ENCODING);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java
index e4b5e88b92..de97b4e0f9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/ExecutionContext.java
@@ -93,9 +93,10 @@
*
* The whole reason for this infrastructure is to:
*
- * 1) avoid passing the {@link PFrame.Reference} and exception state (the currently handled
- * exception) to the callee as arguments, because that would prevent them from being escape analyzed
- * (unless everything is inlined, we do not want to rely on that).
+ * 1) avoid passing the {@link com.oracle.graal.python.builtins.objects.frame.PFrame.Reference} and
+ * exception state (the currently handled exception) to the callee as arguments, because that would
+ * prevent them from being escape analyzed (unless everything is inlined, we do not want to rely on
+ * that).
*
* 2) avoid materializing the {@link PFrame} instance that represents the current
* {@link VirtualFrame}, because it is expensive operation and may prevent objects stored in the
@@ -128,9 +129,11 @@
* pass them initially. First time we actually need them, we do Truffle stack walk
* ({@code TruffleRuntime#iterateFrames}), during which we set the
* {@link PRootNode#getCallerFlags()} flags for all the root nodes that we had to traverse - so next
- * time, we should not need to do the stack walk, we should just receive {@link PFrame.Reference}
- * from the caller and just traverse the linked-list of {@link PFrame.Reference}s to the
- * {@link PFrame.Reference} we need.
+ * time, we should not need to do the stack walk, we should just receive
+ * {@link com.oracle.graal.python.builtins.objects.frame.PFrame.Reference} from the caller and just
+ * traverse the linked-list of
+ * {@link com.oracle.graal.python.builtins.objects.frame.PFrame.Reference}s to the
+ * {@link com.oracle.graal.python.builtins.objects.frame.PFrame.Reference} we need.
*
Python function calls into {@code @TruffleBoundary} annotated code: We need to store the
* exception state and PFrame reference into the thread state. In order to avoid doing this every
* time, flags in {@link IndirectCallData.BoundaryCallData} tells us if we should pass them, and
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
index e1fb2dd535..5ac015a9a9 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
@@ -34,6 +34,8 @@
import static com.oracle.graal.python.builtins.modules.SysModuleBuiltins.T__MULTIARCH;
import static com.oracle.graal.python.builtins.modules.io.IONodes.T_CLOSED;
import static com.oracle.graal.python.builtins.modules.io.IONodes.T_FLUSH;
+import static com.oracle.graal.python.builtins.objects.PythonAbstractObject.NATIVE_POINTER_FREED;
+import static com.oracle.graal.python.builtins.objects.PythonAbstractObject.UNINITIALIZED;
import static com.oracle.graal.python.builtins.objects.str.StringUtils.cat;
import static com.oracle.graal.python.builtins.objects.thread.PThread.GRAALPYTHON_THREADS;
import static com.oracle.graal.python.nodes.BuiltinNames.T_PYEXPAT;
@@ -65,6 +67,7 @@
import static com.oracle.graal.python.nodes.StringLiterals.T_SLASH;
import static com.oracle.graal.python.nodes.StringLiterals.T_WARNINGS;
import static com.oracle.graal.python.nodes.truffle.TruffleStringMigrationHelpers.isJavaString;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached;
import static com.oracle.graal.python.util.PythonUtils.tsLiteral;
@@ -115,14 +118,13 @@
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
+import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionInvoker;
import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
import com.oracle.graal.python.builtins.objects.cext.capi.PThreadState;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper;
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandleContext;
import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem;
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetIterator;
@@ -169,6 +171,8 @@
import com.oracle.graal.python.runtime.exception.PythonExitException;
import com.oracle.graal.python.runtime.exception.PythonThreadKillException;
import com.oracle.graal.python.runtime.locale.PythonLocale;
+import com.oracle.graal.python.runtime.nativeaccess.NativeContext;
+import com.oracle.graal.python.runtime.nativeaccess.NativeMemory;
import com.oracle.graal.python.runtime.object.IDUtils;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.util.Consumer;
@@ -198,6 +202,7 @@
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.GenerateUncached;
+import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.dsl.NonIdempotent;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
@@ -207,6 +212,7 @@
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleString.Encoding;
import com.oracle.truffle.api.utilities.CyclicAssumption;
import com.oracle.truffle.api.utilities.TriState;
import com.oracle.truffle.api.utilities.TruffleWeakReference;
@@ -215,14 +221,27 @@
@Bind.DefaultExpression("get($node)")
public final class PythonContext extends Python3Core {
+ /**
+ * A `PythonAbstractObject` that gets converted to native `nullptr`.
+ */
+ public static final PNone NATIVE_NULL = PNone.NO_VALUE;
+
public static final TruffleString T_IMPLEMENTATION = tsLiteral("implementation");
public static final boolean DEBUG_CAPI = Boolean.getBoolean("python.DebugCAPI");
+ public static final Unsafe UNSAFE = PythonUtils.initUnsafe();
private static final TruffleLogger LOGGER = PythonLanguage.getLogger(PythonContext.class);
+ private static final long SIZEOF_INT64 = 8;
- public final HandleContext nativeContext = new HandleContext(DEBUG_CAPI);
+ public final HandleContext handleContext = new HandleContext(DEBUG_CAPI);
public final NativeBufferContext nativeBufferContext = new NativeBufferContext();
public final ArrowSupport arrowSupport = new ArrowSupport(this);
+
+ /**
+ * List of native memory that should be free'd if this context is finalized.
+ */
+ private List nativeResources;
+
private volatile boolean finalizing;
// Used for testing only.
@@ -248,6 +267,39 @@ public static String getSupportLibName(String libName) {
return getSupportLibName(getPythonOS(), libName);
}
+ /**
+ * Encodes the provided {@link TruffleString} as UTF-8 bytes and copies the bytes (and an
+ * additional NUL char) to a freshly allocated off-heap {@code int8*} (using {@code Unsafe}).
+ *
+ * @param string The string to copy to native.
+ * @param contextMemory If {@code true}, the allocated memory will automatically be released at
+ * context finalization. Otherwise, the caller needs to manually free the memory.
+ */
+ @TruffleBoundary
+ public long stringToNativeUtf8Bytes(TruffleString string, boolean contextMemory) {
+ if (!isNativeAccessAllowed()) {
+ throw CompilerDirectives.shouldNotReachHere();
+ }
+ TruffleString utf8String = string.switchEncodingUncached(Encoding.UTF_8);
+ NativePointer mem;
+ int byteLength = utf8String.byteLength(Encoding.UTF_8);
+ if (contextMemory) {
+ mem = allocateContextMemory(byteLength + 1);
+ NativeMemory.writeByte(mem.asPointer() + byteLength, (byte) 0);
+ } else {
+ mem = NativePointer.wrap(NativeMemory.callocByteArray(byteLength + 1));
+ }
+ utf8String.copyToNativeMemoryUncached(0, mem, 0, byteLength, Encoding.UTF_8);
+ return mem.asPointer();
+ }
+
+ public void ensureNativeAccess() {
+ if (!nativeAccessAllowed) {
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ throw new RuntimeException("Native access not allowed, cannot manipulate native memory");
+ }
+ }
+
/**
* An enum of events which can currently be traced using python's tracing
*/
@@ -338,19 +390,14 @@ public static final class PythonThreadState {
/* corresponds to 'PyThreadState.dict' */
PDict dict;
- /*
- * This is the native wrapper object if we need to expose the thread state as PyThreadState
- * object. We need to store it here because the wrapper may receive 'toNative' in which case
- * a handle is allocated. In order to avoid leaks, the handle needs to be free'd when the
- * owning thread (or the whole context) is disposed.
- */
- PThreadState nativeWrapper;
+ /* The native pointer if we need to expose the thread state as PyThreadState struct. */
+ long nativePointer = UNINITIALIZED;
/*
* Pointer to the native thread-local variable used to store the native PyThreadState struct
* for this thread.
*/
- Object nativeThreadLocalVarPointer;
+ long nativeThreadLocalVarPointer;
/* The global tracing function, set by sys.settrace and returned by sys.gettrace. */
Object traceFun;
@@ -457,12 +504,19 @@ public void setDict(PDict dict) {
this.dict = dict;
}
- public PThreadState getNativeWrapper() {
- return nativeWrapper;
+ public long getNativePointer() {
+ return nativePointer;
}
- public void setNativeWrapper(PThreadState nativeWrapper) {
- this.nativeWrapper = nativeWrapper;
+ public void setNativePointer(long pointer) {
+ assert this.nativePointer == UNINITIALIZED;
+ assert pointer != NATIVE_POINTER_FREED;
+ this.nativePointer = pointer;
+ }
+
+ public void clearNativePointer() {
+ assert this.nativePointer != NATIVE_POINTER_FREED;
+ this.nativePointer = NATIVE_POINTER_FREED;
}
public PContextVarsContext getContextVarsContext(Node node) {
@@ -477,7 +531,7 @@ public void setContextVarsContext(PContextVarsContext contextVarsContext) {
this.contextVarsContext = contextVarsContext;
}
- public void dispose(PythonContext context, boolean canRunGuestCode, boolean clearNativeThreadLocalVarPointer) {
+ public void dispose(boolean canRunGuestCode, boolean clearNativeThreadLocalVarPointer) {
// This method may be called twice on the same object.
/*
@@ -486,29 +540,24 @@ public void dispose(PythonContext context, boolean canRunGuestCode, boolean clea
* 'CApiTransitions.pollReferenceQueue'.
*/
if (dict != null) {
- PythonAbstractObjectNativeWrapper dictNativeWrapper = dict.getNativeWrapper();
- if (dictNativeWrapper != null && dictNativeWrapper.ref == null) {
- CApiTransitions.releaseNativeWrapperUncached(dictNativeWrapper);
- }
- }
- dict = null;
- if (nativeWrapper != null) {
- if (nativeWrapper.ref == null) {
- // There is no PythonObjectReference, this will not be collected anywhere else
- CApiTransitions.releaseNativeWrapperUncached(nativeWrapper);
+ if (dict.isNative() && dict.ref == null) {
+ CApiTransitions.releaseNativeWrapper(dict.getNativePointer());
}
- nativeWrapper = null;
+ dict = null;
}
+
+ PThreadState.dispose(this);
+
/*
* Write 'NULL' to the native thread-local variable used to store the PyThreadState
* struct such that it cannot accidentally be reused. Since this is done as a
* precaution, we just skip this if we cannot run guest code, because it may invoke
* LLVM.
*/
- if (nativeThreadLocalVarPointer != null && canRunGuestCode && clearNativeThreadLocalVarPointer) {
- CStructAccess.WritePointerNode.writeUncached(nativeThreadLocalVarPointer, 0, context.getNativeNull());
+ if (nativeThreadLocalVarPointer != NULLPTR && canRunGuestCode && clearNativeThreadLocalVarPointer) {
+ NativeMemory.writePtr(nativeThreadLocalVarPointer, NULLPTR);
}
- nativeThreadLocalVarPointer = null;
+ nativeThreadLocalVarPointer = NULLPTR;
}
public Object getTraceFun() {
@@ -588,10 +637,9 @@ public void setAsyncgenFirstIter(Object asyncgenFirstIter) {
this.asyncgenFirstIter = asyncgenFirstIter;
}
- public void setNativeThreadLocalVarPointer(Object ptr) {
+ public void setNativeThreadLocalVarPointer(long ptr) {
// either unset or same
- assert nativeThreadLocalVarPointer == null || nativeThreadLocalVarPointer == ptr ||
- InteropLibrary.getUncached().isIdentical(nativeThreadLocalVarPointer, ptr, InteropLibrary.getUncached()) : //
+ assert nativeThreadLocalVarPointer == NULLPTR || nativeThreadLocalVarPointer == ptr : //
String.format("ptr = %s; nativeThreadLocalVarPointer = %s", ptr, nativeThreadLocalVarPointer);
this.nativeThreadLocalVarPointer = ptr;
}
@@ -601,7 +649,7 @@ public Object getNativeThreadLocalVarPointer() {
}
public boolean isNativeThreadStateInitialized() {
- return nativeThreadLocalVarPointer != null;
+ return nativeThreadLocalVarPointer != NULLPTR;
}
}
@@ -623,6 +671,11 @@ private static final class AtExitHook {
@GenerateInline(inlineByDefault = true)
public abstract static class GetThreadStateNode extends Node {
+ @NeverDefault
+ public static GetThreadStateNode create() {
+ return GetThreadStateNodeGen.create();
+ }
+
public static GetThreadStateNode getUncached() {
return GetThreadStateNodeGen.getUncached();
}
@@ -630,7 +683,7 @@ public static GetThreadStateNode getUncached() {
public abstract PythonThreadState execute(Node inliningTarget, PythonContext context);
public final PythonThreadState execute(Node inliningTarget) {
- return execute(inliningTarget, null);
+ return execute(inliningTarget, PythonContext.get(inliningTarget));
}
public final PythonThreadState executeCached(PythonContext context) {
@@ -638,7 +691,7 @@ public final PythonThreadState executeCached(PythonContext context) {
}
public final PythonThreadState executeCached() {
- return executeCached(null);
+ return executeCached(PythonContext.get(this));
}
public final void setTopFrameInfoCached(PythonContext context, PFrame.Reference topframeref) {
@@ -649,23 +702,6 @@ public final void clearTopFrameInfoCached(PythonContext context) {
executeCached(context).topframeref = null;
}
- @Specialization(guards = {"noContext == null", "!curThreadState.isShuttingDown()"})
- @SuppressWarnings("unused")
- static PythonThreadState doNoShutdown(Node inliningTarget, PythonContext noContext,
- @Bind("getThreadState(inliningTarget)") PythonThreadState curThreadState) {
- return curThreadState;
- }
-
- @Specialization(guards = {"noContext == null"}, replaces = "doNoShutdown")
- @InliningCutoff
- PythonThreadState doGeneric(@SuppressWarnings("unused") Node inliningTarget, PythonContext noContext) {
- PythonThreadState curThreadState = PythonLanguage.get(inliningTarget).getThreadStateLocal().get();
- if (curThreadState.isShuttingDown()) {
- throw PythonContext.get(this).killThread();
- }
- return curThreadState;
- }
-
@Specialization(guards = "!curThreadState.isShuttingDown()")
@SuppressWarnings("unused")
static PythonThreadState doNoShutdownWithContext(Node inliningTarget, PythonContext context,
@@ -675,7 +711,7 @@ static PythonThreadState doNoShutdownWithContext(Node inliningTarget, PythonCont
@Specialization(replaces = "doNoShutdownWithContext")
@InliningCutoff
- PythonThreadState doGenericWithContext(Node inliningTarget, PythonContext context) {
+ static PythonThreadState doGenericWithContext(Node inliningTarget, PythonContext context) {
PythonThreadState curThreadState = context.getLanguage(inliningTarget).getThreadStateLocal().get(context.env.getContext());
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, curThreadState.isShuttingDown())) {
throw context.killThread();
@@ -684,7 +720,7 @@ PythonThreadState doGenericWithContext(Node inliningTarget, PythonContext contex
}
@NonIdempotent
- PythonThreadState getThreadState(Node n) {
+ static PythonThreadState getThreadState(Node n) {
return PythonLanguage.get(n).getThreadStateLocal().get();
}
}
@@ -741,7 +777,8 @@ public enum CApiState {
UNINITIALIZED,
INITIALIZING,
INITIALIZED,
- FAILED
+ FAILED,
+ CANNOT_IMPORT
}
/** Initialization state of the C API context. */
@@ -749,6 +786,7 @@ public enum CApiState {
private final ReentrantLock cApiInitializationLock = new ReentrantLock(false);
@CompilationFinal private CApiContext cApiContext;
@CompilationFinal private boolean nativeAccessAllowed;
+ @CompilationFinal private NativeContext nativeContext;
private TruffleString soABI;
@@ -811,8 +849,6 @@ public Thread getOwner() {
// the full module name for package imports
private TruffleString pyPackageContext;
- private final NativePointer nativeNull = NativePointer.createNull();
-
public RootCallTarget signatureContainer;
// Used to store classes registered for interop behavior by the user
@@ -1215,10 +1251,6 @@ public static PythonContext get(Node node) {
return REFERENCE.get(node);
}
- public NativePointer getNativeNull() {
- return nativeNull;
- }
-
public boolean isChildContext() {
return childContextData != null;
}
@@ -2108,12 +2140,16 @@ public void finalizeContext() {
// shut down async actions threads
handler.shutdown();
finalizing = true;
+ if (cApiContext != null) {
+ cApiContext.finalizeCApi(cancelling);
+ }
// interrupt and join or kill python threads
joinPythonThreads();
stdioFlushFailed = flushStdFiles();
- if (cApiContext != null) {
- cApiContext.finalizeCApi();
+ if (nativeContext != null) {
+ nativeContext.close();
}
+ freeContextMemory();
// destroy thread state data, if anything is still running, it will crash now
disposeThreadStates();
}
@@ -2215,7 +2251,7 @@ public void runShutdownHooks() {
private void disposeThreadStates() {
Thread currentThread = Thread.currentThread();
for (Map.Entry entry : threadStateMapping.entrySet()) {
- entry.getValue().dispose(this, true, entry.getKey() == currentThread);
+ entry.getValue().dispose(true, entry.getKey() == currentThread);
}
threadStateMapping.clear();
}
@@ -2712,13 +2748,16 @@ public void initializeNativeThreadState() {
initializeNativeThreadState(getThreadState(getLanguage()));
}
+ private static final CApiTiming TIMING_INIT_THREAD_STATE_CURRENT = CApiTiming.create(true, NativeCAPISymbol.FUN_INIT_THREAD_STATE_CURRENT);
+
@SuppressWarnings("try")
public void initializeNativeThreadState(PythonThreadState pythonThreadState) {
CompilerAsserts.neverPartOfCompilation();
try (GilNode.UncachedAcquire ignored = GilNode.uncachedAcquire()) {
assert getCApiContext() != null;
- Object nativeThreadState = PThreadState.getOrCreateNativeThreadState(pythonThreadState);
- Object nativeThreadLocalVarPointer = PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_INIT_THREAD_STATE_CURRENT, nativeThreadState);
+ long nativeThreadState = PThreadState.getOrCreateNativeThreadState(pythonThreadState);
+ var callable = CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_INIT_THREAD_STATE_CURRENT);
+ long nativeThreadLocalVarPointer = ExternalFunctionInvoker.invokeINIT_THREAD_STATE_CURRENT(TIMING_INIT_THREAD_STATE_CURRENT, callable, nativeThreadState);
pythonThreadState.setNativeThreadLocalVarPointer(nativeThreadLocalVarPointer);
}
}
@@ -2733,7 +2772,7 @@ public void disposeThread(Thread thread, boolean canRunGuestCode) {
}
ts.shutdown();
threadStateMapping.remove(thread);
- ts.dispose(this, canRunGuestCode, thread == Thread.currentThread());
+ ts.dispose(canRunGuestCode, thread == Thread.currentThread());
releaseSentinelLock(ts.sentinelLock);
getSharedMultiprocessingData().removeChildContextThread(PThread.getThreadId(thread));
}
@@ -2749,28 +2788,29 @@ private static void releaseSentinelLock(WeakReference sentinelLockWeakref
}
public CApiState getCApiState() {
- assert cApiContext != null || cApiState == CApiState.UNINITIALIZED || cApiState == CApiState.FAILED : cApiState;
+ assert cApiContext != null || cApiState == CApiState.UNINITIALIZED || cApiState == CApiState.FAILED || cApiState == CApiState.CANNOT_IMPORT : cApiState;
return cApiState;
}
public void setCApiState(CApiState state) {
/*- Allowed transitions:
- * UNINITIALIZED -> INITIALIZING, FAILED
- * INITIALIZING -> INITIALIZED, FAILED
+ * UNINITIALIZED -> INITIALIZING, FAILED, CANNOT_IMPORT
+ * INITIALIZING -> INITIALIZED, FAILED, CANNOT_IMPORT
*/
assert state != CApiState.UNINITIALIZED;
assert cApiInitializationLock.isHeldByCurrentThread();
assert state != CApiState.INITIALIZING || cApiContext != null;
assert state != CApiState.INITIALIZED || cApiContext != null;
- assert cApiState != CApiState.UNINITIALIZED || state == CApiState.INITIALIZING || state == CApiState.FAILED;
- assert cApiState != CApiState.INITIALIZING || state == CApiState.INITIALIZED || state == CApiState.FAILED;
+ assert cApiState != CApiState.UNINITIALIZED || state == CApiState.INITIALIZING || state == CApiState.FAILED || state == CApiState.CANNOT_IMPORT;
+ assert cApiState != CApiState.INITIALIZING || state == CApiState.INITIALIZED || state == CApiState.FAILED || state == CApiState.CANNOT_IMPORT;
assert cApiState != CApiState.INITIALIZED;
assert cApiState != CApiState.FAILED;
+ assert cApiState != CApiState.CANNOT_IMPORT;
cApiState = state;
}
public CApiContext getCApiContext() {
- assert cApiContext != null || cApiState == CApiState.UNINITIALIZED || cApiState == CApiState.FAILED;
+ assert cApiContext != null || cApiState == CApiState.UNINITIALIZED || cApiState == CApiState.FAILED || cApiState == CApiState.CANNOT_IMPORT;
return cApiContext;
}
@@ -2784,6 +2824,15 @@ public void setCApiContext(CApiContext capiContext) {
this.cApiContext = capiContext;
}
+ public NativeContext ensureNativeContext() {
+ if (nativeContext == null) {
+ ensureNativeAccess();
+ CompilerDirectives.transferToInterpreterAndInvalidate();
+ nativeContext = NativeContext.create();
+ }
+ return nativeContext;
+ }
+
public void runCApiHooks() {
for (Runnable capiHook : capiHooks) {
capiHook.run();
@@ -2882,52 +2931,40 @@ public Unsafe getUnsafe() {
throw new RuntimeException("Native access not allowed, cannot manipulate native memory");
}
- public long allocateNativeMemory(long size) {
- return allocateNativeMemoryBoundary(getUnsafe(), size);
- }
-
- @TruffleBoundary
- private static long allocateNativeMemoryBoundary(Unsafe unsafe, long size) {
- return unsafe.allocateMemory(size);
- }
-
- public void freeNativeMemory(long address) {
- freeNativeMemoryBoundary(getUnsafe(), address);
- }
-
- @TruffleBoundary
- private static void freeNativeMemoryBoundary(Unsafe unsafe, long address) {
- unsafe.freeMemory(address);
- }
-
- public void copyNativeMemory(long dst, byte[] src, int srcOffset, int size) {
- copyNativeMemoryBoundary(getUnsafe(), null, dst, src, byteArrayOffset(srcOffset), size);
- }
-
- public void copyNativeMemory(byte[] dst, int dstOffset, long src, int size) {
- copyNativeMemoryBoundary(getUnsafe(), dst, byteArrayOffset(dstOffset), null, src, size);
- }
-
- private static long byteArrayOffset(int offset) {
- return (long) Unsafe.ARRAY_BYTE_BASE_OFFSET + (long) Unsafe.ARRAY_BYTE_INDEX_SCALE * (long) offset;
- }
-
- @TruffleBoundary
- private static void copyNativeMemoryBoundary(Unsafe unsafe, Object dst, long dstOffset, Object src, long srcOffset, int size) {
- unsafe.copyMemory(src, srcOffset, dst, dstOffset, size);
- }
-
- public void setNativeMemory(long pointer, int size, byte value) {
- setNativeMemoryBoundary(getUnsafe(), pointer, size, value);
+ @SuppressWarnings("AssertWithSideEffects")
+ public static void setWasStackWalk() {
+ assert (PythonContext.get(null).wasStackWalk = true);
}
+ /**
+ * Allocates native memory that will be free'd if the context is disposed.
+ *
+ * @param byteSize Number of bytes to allocate.
+ * @return An interop pointer.
+ */
@TruffleBoundary
- private static void setNativeMemoryBoundary(Unsafe unsafe, long pointer, int size, byte value) {
- unsafe.setMemory(pointer, size, value);
+ public NativePointer allocateContextMemory(int byteSize) {
+ ensureNativeAccess();
+ if (nativeResources == null) {
+ nativeResources = new LinkedList<>();
+ }
+ NativePointer nativePointer = NativePointer.wrap(NativeMemory.malloc(byteSize));
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(String.format("Allocated %d bytes of context memory: %s", byteSize, nativePointer));
+ }
+ nativeResources.add(nativePointer);
+ return nativePointer;
}
- @SuppressWarnings("AssertWithSideEffects")
- public static void setWasStackWalk() {
- assert (PythonContext.get(null).wasStackWalk = true);
+ private void freeContextMemory() {
+ if (nativeResources != null) {
+ ensureNativeAccess();
+ for (NativePointer nativePointer : nativeResources) {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.fine(String.format("Freeing context memory: %s", nativePointer));
+ }
+ NativeMemory.free(nativePointer.asPointer());
+ }
+ }
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java
index fb7fdb25f0..fc8c4d05a5 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java
@@ -408,9 +408,6 @@ public static void checkBytecodeDSLEnv() {
@EngineOption @Option(category = OptionCategory.EXPERT, usageSyntax = "true|false", help = "Record a lightweight rolling history of GraalPy raw native memory allocation sites for allocator debugging.", stability = OptionStability.EXPERIMENTAL) //
public static final OptionKey SampleNativeMemoryAllocSites = new OptionKey<>(false);
- @Option(category = OptionCategory.EXPERT, usageSyntax = "true|false", help = "Use the panama backend for NFI.", stability = OptionStability.EXPERIMENTAL) //
- public static final OptionKey UsePanama = new OptionKey<>(false); // see [GR-67358]
-
@Option(category = OptionCategory.EXPERT, usageSyntax = "true|false", help = "Set by the launcher to true (false means that GraalPy is being embedded in an application).") //
public static final OptionKey RunViaLauncher = new OptionKey<>(false);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java
new file mode 100644
index 0000000000..643e58157d
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2025, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.runtime.nativeaccess;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import org.graalvm.nativeimage.ImageInfo;
+
+public abstract class NativeAccessSupport {
+ private static final NativeAccessSupport INSTANCE = createImpl();
+
+ protected NativeAccessSupport() {
+ }
+
+ private static NativeAccessSupport createImpl() {
+ if (!ImageInfo.inImageCode() && Runtime.version().feature() < 22) {
+ return new NativeAccessSupportJdk21();
+ }
+ return new NativeAccessSupportJdk22Gen();
+ }
+
+ protected static UnsupportedOperationException unsupported() {
+ return new UnsupportedOperationException(NativeContext.UNAVAILABLE);
+ }
+
+ protected static MethodHandle unsupportedDowncallHandle(MethodType methodType) {
+ MethodHandle methodHandle = MethodHandles.throwException(methodType.returnType(), UnsupportedOperationException.class);
+ methodHandle = MethodHandles.insertArguments(methodHandle, 0, unsupported());
+ return MethodHandles.dropArguments(methodHandle, 0, methodType.parameterList());
+ }
+
+ protected static Class> asJavaType(NativeSimpleType type) {
+ return switch (type) {
+ case VOID -> void.class;
+ case SINT8 -> byte.class;
+ case SINT16 -> short.class;
+ case SINT32 -> int.class;
+ case SINT64 -> long.class;
+ case FLOAT -> float.class;
+ case DOUBLE -> double.class;
+ case RAW_POINTER -> long.class;
+ };
+ }
+
+ static Object createArena() {
+ return INSTANCE.createArenaImpl();
+ }
+
+ static void closeArena(Object arena) {
+ INSTANCE.closeArenaImpl(arena);
+ }
+
+ static NativeLibraryLookup libraryLookup(String name, Object arena) {
+ return INSTANCE.libraryLookupImpl(name, arena);
+ }
+
+ static long lookupSymbol(NativeLibraryLookup lookup, String name) {
+ return lookup.find(name).orElseThrow();
+ }
+
+ static long lookupDefault(String name) {
+ return INSTANCE.lookupDefaultImpl(name);
+ }
+
+ static MethodHandle createDowncallHandle(NativeSimpleType resType, NativeSimpleType... argTypes) {
+ return INSTANCE.createTypedDowncallHandle(resType, argTypes);
+ }
+
+ public static MethodHandle createDowncallHandle(MethodType methodType, boolean critical) {
+ return INSTANCE.createDowncallHandleImpl(methodType, critical);
+ }
+
+ static long createClosure(MethodHandle staticMethodHandle, NativeSimpleType resType, NativeSimpleType[] argTypes, Object arena) {
+ return INSTANCE.createClosureImpl(staticMethodHandle, resType, argTypes, arena);
+ }
+
+ private MethodHandle createTypedDowncallHandle(NativeSimpleType resType, NativeSimpleType... argTypes) {
+ Class>[] parameterTypes = new Class>[argTypes.length + 1];
+ parameterTypes[0] = long.class;
+ for (int i = 0; i < argTypes.length; i++) {
+ parameterTypes[i + 1] = asJavaType(argTypes[i]);
+ }
+ return createDowncallHandleImpl(MethodType.methodType(asJavaType(resType), parameterTypes), false);
+ }
+
+ protected abstract Object createArenaImpl();
+
+ protected abstract void closeArenaImpl(Object arena);
+
+ protected abstract NativeLibraryLookup libraryLookupImpl(String name, Object arena);
+
+ protected abstract long lookupDefaultImpl(String name);
+
+ protected abstract MethodHandle createDowncallHandleImpl(MethodType methodType, boolean critical);
+
+ protected abstract long createClosureImpl(MethodHandle staticMethodHandle, NativeSimpleType resType, NativeSimpleType[] argTypes, Object arena);
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/GetNextVaArgNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java
similarity index 59%
rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/GetNextVaArgNode.java
rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java
index c72bb617ce..3e006c9a77 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/GetNextVaArgNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -38,36 +38,38 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package com.oracle.graal.python.builtins.objects.cext.common;
+package com.oracle.graal.python.runtime.nativeaccess;
-import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PCallCapiFunction;
-import com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol;
-import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.GenerateCached;
-import com.oracle.truffle.api.dsl.GenerateInline;
-import com.oracle.truffle.api.dsl.GenerateUncached;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.nodes.Node;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
-/**
- * Gets the pointer to the outVar at the given index. This is basically an access to the varargs
- * like {@code va_arg(*valist, void *)}
- */
-@GenerateInline
-@GenerateCached(false)
-@GenerateUncached
-public abstract class GetNextVaArgNode extends Node {
+final class NativeAccessSupportJdk21 extends NativeAccessSupport {
+ @Override
+ protected Object createArenaImpl() {
+ return null;
+ }
+
+ @Override
+ protected void closeArenaImpl(Object arena) {
+ }
- public abstract Object execute(Node inliningTarget, Object valist) throws InteropException;
+ @Override
+ protected NativeLibraryLookup libraryLookupImpl(String name, Object arena) {
+ throw unsupported();
+ }
+
+ @Override
+ protected long lookupDefaultImpl(String name) {
+ throw unsupported();
+ }
- public static Object executeUncached(Object valist) throws InteropException {
- return GetNextVaArgNodeGen.getUncached().execute(null, valist);
+ @Override
+ protected MethodHandle createDowncallHandleImpl(MethodType methodType, boolean critical) {
+ return unsupportedDowncallHandle(methodType);
}
- @Specialization
- static Object doGeneric(Object valist,
- @Cached(inline = false) PCallCapiFunction nextNode) {
- return nextNode.call(NativeCAPISymbol.FUN_VA_ARG_POINTER, valist);
+ @Override
+ protected long createClosureImpl(MethodHandle staticMethodHandle, NativeSimpleType resType, NativeSimpleType[] argTypes, Object arena) {
+ throw unsupported();
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java
new file mode 100644
index 0000000000..6c54eeda78
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2025, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.runtime.nativeaccess;
+
+import java.lang.invoke.MethodHandle;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import com.oracle.graal.python.PythonLanguage;
+import com.oracle.graal.python.annotations.PythonOS;
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+
+public final class NativeContext {
+ public static final String UNAVAILABLE = "JEP 454 is not included on this JDK, this prevents loading native extensions modules.";
+
+ private static final int LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100;
+ private static final int LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200;
+ private static final int LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400;
+ private static final int LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800;
+ private static final int LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;
+ private static final int WINDOWS_LOAD_LIBRARY_SEARCH_MASK = LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_USER_DIRS |
+ LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
+ private static final int WINDOWS_DEFAULT_LOAD_LIBRARY_FLAGS = LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR;
+ private static final int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
+ private static final int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
+ private static final int FORMAT_MESSAGE_BUFFER_CHARS = 2048;
+
+ private final ConcurrentLinkedQueue libraries = new ConcurrentLinkedQueue<>();
+ final Object arena;
+
+ public static NativeContext create() {
+ return new NativeContext();
+ }
+
+ @TruffleBoundary
+ NativeContext() {
+ arena = NativeAccessSupport.createArena();
+ }
+
+ public void close() {
+ CompilerAsserts.neverPartOfCompilation();
+ for (NativeLibrary library : libraries) {
+ int result;
+ try {
+ result = isWindows() ? (int) FREE_LIBRARY.invokeExact(freeLibraryPtr, library.ptr) : (int) DLCLOSE.invokeExact(dlclosePtr, library.ptr);
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ if (result != 0) {
+ // TODO(native-access) log error
+ }
+ }
+ NativeAccessSupport.closeArena(arena);
+ }
+
+ public NativeLibrary loadLibrary(String name, int flags) throws NativeLibraryLoadException {
+ CompilerAsserts.neverPartOfCompilation();
+
+ // This needs to be done first and may fail if the executing JDK does not support FFM API.
+ ensureLoader();
+
+ long lib;
+ long nativeName = isWindows() ? NativeMemory.javaStringToNativeUtf16(name) : NativeMemory.javaStringToNativeUtf8(name);
+ try {
+ if (isWindows()) {
+ int callFlags = sanitizeWindowsLoadLibraryFlags(flags) | WINDOWS_DEFAULT_LOAD_LIBRARY_FLAGS;
+ lib = (long) LOAD_LIBRARY_EX.invokeExact(loadLibraryExPtr, nativeName, 0L, callFlags);
+ } else {
+ int callFlags = flags;
+ if ((callFlags & (RTLD_LAZY | RTLD_NOW)) == 0) {
+ callFlags |= RTLD_NOW;
+ }
+ lib = (long) DLOPEN.invokeExact(dlopenPtr, nativeName, callFlags);
+ }
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ } finally {
+ NativeMemory.free(nativeName);
+ }
+ if (lib == 0) {
+ throw createLoadLibraryException();
+ }
+ NativeLibrary library = new NativeLibrary(this, lib);
+ libraries.add(library);
+ return library;
+ }
+
+ @SuppressWarnings("static-method")
+ long lookupOptionalSymbol(long library, String name) {
+ // TODO(native-access) if logging enabled, keep track of ptr->name mappings
+ long nativeName = NativeMemory.javaStringToNativeUtf8(name);
+ try {
+ return isWindows() ? (long) GET_PROC_ADDRESS.invokeExact(getProcAddressPtr, library, nativeName) : (long) DLSYM.invokeExact(dlsymPtr, library, nativeName);
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ } finally {
+ NativeMemory.free(nativeName);
+ }
+ }
+
+ private static boolean isWindows() {
+ return PythonLanguage.getPythonOS() == PythonOS.PLATFORM_WIN32;
+ }
+
+ // TODO(native-access) platform-specific values for RTLD_* constants
+ private static final int RTLD_LAZY = 1;
+ private static final int RTLD_NOW = 2;
+
+ private static final MethodHandle DLOPEN = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.RAW_POINTER, NativeSimpleType.SINT32);
+ private static final MethodHandle DLCLOSE = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT64);
+ private static final MethodHandle DLSYM = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.SINT64, NativeSimpleType.RAW_POINTER);
+ private static final MethodHandle LOAD_LIBRARY_EX = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER,
+ NativeSimpleType.SINT32);
+ private static final MethodHandle FREE_LIBRARY = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT64);
+ private static final MethodHandle GET_PROC_ADDRESS = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.SINT64, NativeSimpleType.RAW_POINTER);
+ private static final MethodHandle GET_LAST_ERROR = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32);
+ private static final MethodHandle FORMAT_MESSAGE = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT32, NativeSimpleType.RAW_POINTER, NativeSimpleType.SINT32,
+ NativeSimpleType.SINT32,
+ NativeSimpleType.RAW_POINTER, NativeSimpleType.SINT32, NativeSimpleType.RAW_POINTER);
+ private static final MethodHandle DLERROR = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64);
+
+ private static long dlopenPtr;
+ private static long dlclosePtr;
+ private static long dlsymPtr;
+ private static long dlerrorPtr;
+ private static long loadLibraryExPtr;
+ private static long freeLibraryPtr;
+ private static long getProcAddressPtr;
+ private static long getLastErrorPtr;
+ private static long formatMessagePtr;
+ private static Object windowsLookupArena;
+ private static NativeLibraryLookup windowsLookup;
+
+ private static void ensureLoader() throws UnsupportedOperationException {
+ if (isWindows()) {
+ if (loadLibraryExPtr != 0) {
+ assert freeLibraryPtr != 0;
+ assert getProcAddressPtr != 0;
+ assert getLastErrorPtr != 0;
+ assert formatMessagePtr != 0;
+ return;
+ }
+ if (windowsLookup == null) {
+ windowsLookupArena = NativeAccessSupport.createArena();
+ windowsLookup = NativeAccessSupport.libraryLookup("kernel32", windowsLookupArena);
+ }
+ loadLibraryExPtr = NativeAccessSupport.lookupSymbol(windowsLookup, "LoadLibraryExW");
+ freeLibraryPtr = NativeAccessSupport.lookupSymbol(windowsLookup, "FreeLibrary");
+ getProcAddressPtr = NativeAccessSupport.lookupSymbol(windowsLookup, "GetProcAddress");
+ getLastErrorPtr = NativeAccessSupport.lookupSymbol(windowsLookup, "GetLastError");
+ formatMessagePtr = NativeAccessSupport.lookupSymbol(windowsLookup, "FormatMessageW");
+ return;
+ }
+ dlopenPtr = NativeAccessSupport.lookupDefault("dlopen");
+ dlclosePtr = NativeAccessSupport.lookupDefault("dlclose");
+ dlsymPtr = NativeAccessSupport.lookupDefault("dlsym");
+ dlerrorPtr = NativeAccessSupport.lookupDefault("dlerror");
+ }
+
+ private static int sanitizeWindowsLoadLibraryFlags(int flags) {
+ return flags & WINDOWS_LOAD_LIBRARY_SEARCH_MASK;
+ }
+
+ @TruffleBoundary
+ private static NativeLibraryLoadException createLoadLibraryException() {
+ if (isWindows()) {
+ int errorCode = getLastError();
+ String detail = formatWindowsError(errorCode);
+ if (detail == null || detail.isBlank()) {
+ return new NativeLibraryLoadException("Windows error " + errorCode);
+ }
+ return new NativeLibraryLoadException("Windows error " + errorCode + ": " + detail);
+ }
+ String detail = getDlError();
+ if (detail == null || detail.isBlank()) {
+ return new NativeLibraryLoadException("dlopen failed");
+ }
+ return new NativeLibraryLoadException(detail);
+ }
+
+ private static int getLastError() {
+ try {
+ return (int) GET_LAST_ERROR.invokeExact(getLastErrorPtr);
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ }
+
+ private static String getDlError() {
+ try {
+ long ptr = (long) DLERROR.invokeExact(dlerrorPtr);
+ if (ptr == 0) {
+ return null;
+ }
+ return NativeMemory.zeroTerminatedUtf8ToJavaString(ptr);
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ }
+
+ private static String formatWindowsError(int errorCode) {
+ long buffer = NativeMemory.callocShortArray(FORMAT_MESSAGE_BUFFER_CHARS);
+ try {
+ int result;
+ try {
+ result = (int) FORMAT_MESSAGE.invokeExact(formatMessagePtr, FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0L, errorCode, 0, buffer,
+ FORMAT_MESSAGE_BUFFER_CHARS, 0L);
+ } catch (Throwable e) {
+ throw CompilerDirectives.shouldNotReachHere(e);
+ }
+ if (result == 0) {
+ return null;
+ }
+ return NativeMemory.zeroTerminatedUtf16ToJavaString(buffer).stripTrailing();
+ } finally {
+ NativeMemory.free(buffer);
+ }
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/GetReplacementNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeFunctionPointer.java
similarity index 53%
rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/GetReplacementNode.java
rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeFunctionPointer.java
index 93c82d49e6..217975cfcd 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/GetReplacementNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeFunctionPointer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -38,45 +38,53 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package com.oracle.graal.python.builtins.objects.cext.capi.transitions;
+package com.oracle.graal.python.runtime.nativeaccess;
-import com.oracle.graal.python.builtins.objects.cext.capi.PyMemoryViewWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonClassNativeWrapper;
-import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.GenerateCached;
-import com.oracle.truffle.api.dsl.GenerateInline;
-import com.oracle.truffle.api.dsl.GenerateUncached;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.interop.InteropLibrary;
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
/**
- * Native wrappers are usually materialized lazily when they receive
- * {@link InteropLibrary#toNative(Object)}. A few native wrappers may emulate data structures where
- * it is more efficient to have off-heap memory that just replaces the object on the native side
- * (and is presumably somehow synced). These wrappers have specializations here so the users of this
- * node can return them directly for native access.
+ * A native function pointer represented by its raw address and function pointer type. The type is
+ * stored as the native return type plus the native argument types.
*/
-@GenerateUncached
-@GenerateInline
-@GenerateCached(false)
-public abstract class GetReplacementNode extends Node {
+public final class NativeFunctionPointer {
+ private final long ptr;
+ private final NativeSimpleType resType;
+ private final NativeSimpleType[] argTypes;
- public abstract Object execute(Node inliningTarget, PythonNativeWrapper wrapper);
+ private NativeFunctionPointer(long ptr, NativeSimpleType resType, NativeSimpleType[] argTypes) {
+ this.ptr = ptr;
+ this.resType = resType;
+ this.argTypes = argTypes;
+ }
+
+ public static NativeFunctionPointer create(@SuppressWarnings("unused") NativeContext context, long pointer, NativeSimpleType resType, NativeSimpleType... argTypes) {
+ // TODO(native-access) if logging enabled, use context to lookup name
+ return new NativeFunctionPointer(pointer, resType, argTypes.clone());
+ }
- @Specialization
- static Object doReplacingWrapper(PyMemoryViewWrapper wrapper) {
- return wrapper.getReplacement();
+ public long getAddress() {
+ return ptr;
}
- @Specialization
- static Object doReplacingWrapper(PythonClassNativeWrapper wrapper) {
- return wrapper.getReplacement();
+ @Override
+ @TruffleBoundary
+ public String toString() {
+ return "NativeFunctionPointer[" +
+ "ptr=" + ptr + ", " +
+ "signature=" + toSignatureString() + ']';
}
- @Fallback
- static Object doWrapper(Node inliningTarget, PythonNativeWrapper wrapper) {
- return null;
+ @TruffleBoundary
+ private String toSignatureString() {
+ StringBuilder sb = new StringBuilder("(");
+ for (int i = 0; i < argTypes.length; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(argTypes[i]);
+ }
+ sb.append("): ");
+ sb.append(resType);
+ return sb.toString();
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibrary.java
similarity index 71%
rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiGuards.java
rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibrary.java
index 86adf483e4..996ea67ad3 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiGuards.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibrary.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2025, 2026, 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
@@ -38,25 +38,29 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package com.oracle.graal.python.builtins.objects.cext.capi;
+package com.oracle.graal.python.runtime.nativeaccess;
-import com.oracle.graal.python.builtins.objects.cext.common.NativePointer;
+import com.oracle.truffle.api.CompilerDirectives;
-public abstract class CApiGuards {
+public final class NativeLibrary {
- public static boolean isPrimitiveNativeWrapper(Object object) {
- return object instanceof PrimitiveNativeWrapper;
- }
+ private final NativeContext context;
+ final long ptr;
- public static boolean isNativeWrapper(Object object) {
- return object instanceof PythonNativeWrapper || object instanceof PyCFunctionWrapper;
+ NativeLibrary(NativeContext context, long ptr) {
+ this.context = context;
+ this.ptr = ptr;
}
- public static boolean isNativeNull(Object object) {
- return object instanceof NativePointer nativePointer && nativePointer.isNull();
+ public long lookupSymbol(String name) {
+ long symbol = lookupOptionalSymbol(name);
+ if (symbol == 0) {
+ throw CompilerDirectives.shouldNotReachHere("symbol not found: " + name);
+ }
+ return symbol;
}
- public static boolean isSpecialSingleton(Object delegate) {
- return CApiContext.getSingletonNativeWrapperIdx(delegate) != -1;
+ public long lookupOptionalSymbol(String name) {
+ return context.lookupOptionalSymbol(ptr, name);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibraryLoadException.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibraryLoadException.java
new file mode 100644
index 0000000000..d5da82b334
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibraryLoadException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.runtime.nativeaccess;
+
+public final class NativeLibraryLoadException extends Exception {
+ private static final long serialVersionUID = 8768143107513538801L;
+
+ public NativeLibraryLoadException(String message) {
+ super(message);
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtAsPythonObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibraryLookup.java
similarity index 86%
rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtAsPythonObjectNode.java
rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibraryLookup.java
index 0bde2f0bc7..4d762e82e7 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/CExtAsPythonObjectNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeLibraryLookup.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2026, 2026, 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
@@ -38,11 +38,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package com.oracle.graal.python.builtins.objects.cext.common;
+package com.oracle.graal.python.runtime.nativeaccess;
-import com.oracle.graal.python.nodes.PNodeWithContext;
+import java.util.OptionalLong;
-public abstract class CExtAsPythonObjectNode extends PNodeWithContext {
-
- public abstract Object execute(Object object);
+@FunctionalInterface
+public interface NativeLibraryLookup {
+ OptionalLong find(String name);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeMemory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeMemory.java
new file mode 100644
index 0000000000..dd6dca5e49
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeMemory.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2025, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.runtime.nativeaccess;
+
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+
+import sun.misc.Unsafe;
+
+public final class NativeMemory {
+ public static final long NULLPTR = 0L;
+ public static final long POINTER_SIZE = Long.BYTES;
+
+ static final Unsafe UNSAFE = initUnsafe();
+
+ private NativeMemory() {
+ }
+
+ @TruffleBoundary
+ public static long malloc(long size) {
+ assert size > 0;
+ return UNSAFE.allocateMemory(size);
+ }
+
+ @TruffleBoundary
+ public static long calloc(long size) {
+ long ptr = malloc(size);
+ memset(ptr, (byte) 0, size);
+ return ptr;
+ }
+
+ public static void free(long ptr) {
+ UNSAFE.freeMemory(ptr);
+ }
+
+ public static void memcpy(long dst, long src, long size) {
+ UNSAFE.copyMemory(null, src, null, dst, size);
+ }
+
+ public static void memset(long dst, byte value, long count) {
+ UNSAFE.setMemory(dst, count, value);
+ }
+
+ /**
+ * Expects zero-terminated byte arrays.
+ */
+ public static int strcmp(long a, long b) {
+ for (long i = 0;; i++) {
+ byte ba = UNSAFE.getByte(null, a + i);
+ byte bb = UNSAFE.getByte(null, b + i);
+ int diff = (ba & 0xFF) - (bb & 0xFF);
+ if (ba == 0 || diff != 0) {
+ return diff;
+ }
+ }
+ }
+
+ public static long mallocByteArray(long count) {
+ assert count > 0;
+ return malloc(count);
+ }
+
+ public static long callocByteArray(long count) {
+ assert count > 0;
+ return calloc(count);
+ }
+
+ public static long mallocShortArray(long count) {
+ assert count > 0;
+ assert canMultiplyWithoutOverflow(count, Short.BYTES);
+ return malloc(count * Short.BYTES);
+ }
+
+ public static long callocShortArray(long count) {
+ assert count > 0;
+ assert canMultiplyWithoutOverflow(count, Short.BYTES);
+ return calloc(count * Short.BYTES);
+ }
+
+ public static long mallocIntArray(long count) {
+ assert count > 0;
+ assert canMultiplyWithoutOverflow(count, Integer.BYTES);
+ return malloc(count * Integer.BYTES);
+ }
+
+ public static long callocIntArray(long count) {
+ assert count > 0;
+ assert canMultiplyWithoutOverflow(count, Integer.BYTES);
+ return calloc(count * Integer.BYTES);
+ }
+
+ public static long mallocLongArray(long count) {
+ assert count > 0;
+ assert canMultiplyWithoutOverflow(count, Long.BYTES);
+ return malloc(count * Long.BYTES);
+ }
+
+ public static long callocLongArray(long count) {
+ assert count > 0;
+ assert canMultiplyWithoutOverflow(count, Long.BYTES);
+ return calloc(count * Long.BYTES);
+ }
+
+ public static long mallocPtrArray(long count) {
+ assert count > 0;
+ assert canMultiplyWithoutOverflow(count, (int) POINTER_SIZE);
+ return malloc(count * POINTER_SIZE);
+ }
+
+ public static long callocPtrArray(long count) {
+ assert count > 0;
+ assert canMultiplyWithoutOverflow(count, (int) POINTER_SIZE);
+ return calloc(count * POINTER_SIZE);
+ }
+
+ public static long getFieldPtr(long basePtr, long offset) {
+ assert basePtr >= 0 && offset >= 0 && basePtr + offset >= 0;
+ return basePtr + offset;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public static byte readByte(long pointer) {
+ return UNSAFE.getByte(pointer);
+ }
+
+ public static void writeByte(long pointer, byte value) {
+ UNSAFE.putByte(pointer, value);
+ }
+
+ public static byte readByteArrayElement(long arrayPtr, long index) {
+ return readByte(arrayPtr + index);
+ }
+
+ public static void writeByteArrayElement(long arrayPtr, long index, byte value) {
+ writeByte(arrayPtr + index, value);
+ }
+
+ public static byte[] readByteArrayElements(long arrayPtr, long srcIndex, int count) {
+ byte[] result = new byte[count];
+ readByteArrayElements(arrayPtr, srcIndex, result, 0, count);
+ return result;
+ }
+
+ public static void readByteArrayElements(long arrayPtr, long srcIndex, byte[] dst, int dstIndex, int count) {
+ UNSAFE.copyMemory(null, arrayPtr + srcIndex, dst, Unsafe.ARRAY_BYTE_BASE_OFFSET + (long) dstIndex, count);
+ }
+
+ public static void writeByteArrayElements(long arrayPtr, long dstIndex, byte[] src, int offset, int count) {
+ UNSAFE.copyMemory(src, Unsafe.ARRAY_BYTE_BASE_OFFSET + (long) offset, null, arrayPtr + dstIndex, count);
+ }
+
+ public static void copyByteArray(long dstArray, long dstIndex, long srcArray, long srcIndex, long count) {
+ memcpy(dstArray + dstIndex, srcArray + srcIndex, count);
+ }
+
+ public static short readShort(long pointer) {
+ return UNSAFE.getShort(pointer);
+ }
+
+ public static void writeShort(long pointer, short value) {
+ UNSAFE.putShort(pointer, value);
+ }
+
+ public static short readShortArrayElement(long arrayPtr, long index) {
+ assert canMultiplyWithoutOverflow(index, Short.BYTES);
+ return readShort(arrayPtr + index * Short.BYTES);
+ }
+
+ public static void writeShortArrayElement(long arrayPtr, long index, short value) {
+ assert canMultiplyWithoutOverflow(index, Short.BYTES);
+ writeShort(arrayPtr + index * Short.BYTES, value);
+ }
+
+ public static int readInt(long pointer) {
+ return UNSAFE.getInt(pointer);
+ }
+
+ public static void writeInt(long pointer, int value) {
+ UNSAFE.putInt(pointer, value);
+ }
+
+ public static int readIntArrayElement(long arrayPtr, long index) {
+ assert canMultiplyWithoutOverflow(index, Integer.BYTES);
+ return readInt(arrayPtr + index * Integer.BYTES);
+ }
+
+ public static void writeIntArrayElement(long arrayPtr, long index, int value) {
+ assert canMultiplyWithoutOverflow(index, Integer.BYTES);
+ writeInt(arrayPtr + index * Integer.BYTES, value);
+ }
+
+ public static long readLong(long pointer) {
+ return UNSAFE.getLong(pointer);
+ }
+
+ public static void writeLong(long pointer, long value) {
+ UNSAFE.putLong(pointer, value);
+ }
+
+ public static long readLongArrayElement(long arrayPtr, long index) {
+ assert canMultiplyWithoutOverflow(index, Long.BYTES);
+ return readLong(arrayPtr + index * Long.BYTES);
+ }
+
+ public static void writeLongArrayElement(long arrayPtr, long index, long value) {
+ assert canMultiplyWithoutOverflow(index, Long.BYTES);
+ writeLong(arrayPtr + index * Long.BYTES, value);
+ }
+
+ public static long[] readLongArrayElements(long arrayPtr, long srcIndex, int count) {
+ long[] result = new long[count];
+ readLongArrayElements(arrayPtr, srcIndex, result, 0, count);
+ return result;
+ }
+
+ public static void readLongArrayElements(long arrayPtr, long srcIndex, long[] dst, int dstIndex, int count) {
+ assert canMultiplyWithoutOverflow(srcIndex, Long.BYTES);
+ UNSAFE.copyMemory(null, arrayPtr + srcIndex * Long.BYTES, dst, Unsafe.ARRAY_LONG_BASE_OFFSET + (long) dstIndex * Long.BYTES, (long) count * Long.BYTES);
+ }
+
+ public static long readPtr(long pointer) {
+ return UNSAFE.getLong(pointer);
+ }
+
+ public static void writePtr(long pointer, long value) {
+ UNSAFE.putLong(pointer, value);
+ }
+
+ public static long readPtrArrayElement(long arrayPtr, long index) {
+ assert canMultiplyWithoutOverflow(index, (int) POINTER_SIZE);
+ return readPtr(arrayPtr + index * POINTER_SIZE);
+ }
+
+ public static void writePtrArrayElement(long arrayPtr, long index, long value) {
+ assert canMultiplyWithoutOverflow(index, (int) POINTER_SIZE);
+ writePtr(arrayPtr + index * POINTER_SIZE, value);
+ }
+
+ public static void writePtrArrayElements(long arrayPtr, long dstIndex, long[] src, int offset, int count) {
+ assert canMultiplyWithoutOverflow(dstIndex, (int) POINTER_SIZE);
+ UNSAFE.copyMemory(src, Unsafe.ARRAY_LONG_BASE_OFFSET + offset * POINTER_SIZE, null, arrayPtr + dstIndex * POINTER_SIZE, count * POINTER_SIZE);
+ }
+
+ public static void copyPtrArray(long dstArray, long dstIndex, long srcArray, long srcIndex, long count) {
+ assert canMultiplyWithoutOverflow(dstIndex, (int) POINTER_SIZE);
+ assert canMultiplyWithoutOverflow(srcIndex, (int) POINTER_SIZE);
+ assert canMultiplyWithoutOverflow(count, (int) POINTER_SIZE);
+ memcpy(dstArray + dstIndex * POINTER_SIZE, srcArray + srcIndex * POINTER_SIZE, count * POINTER_SIZE);
+ }
+
+ public static float readFloat(long pointer) {
+ return UNSAFE.getFloat(pointer);
+ }
+
+ public static void writeFloat(long pointer, float value) {
+ UNSAFE.putFloat(pointer, value);
+ }
+
+ public static double readDouble(long pointer) {
+ return UNSAFE.getDouble(pointer);
+ }
+
+ public static void writeDouble(long pointer, double value) {
+ UNSAFE.putDouble(pointer, value);
+ }
+
+ static long javaStringToNativeUtf8(String s) {
+ byte[] utf8 = s.getBytes(StandardCharsets.UTF_8);
+ long ptr = NativeMemory.malloc(utf8.length + 1);
+ UNSAFE.copyMemory(utf8, UNSAFE.arrayBaseOffset(byte[].class), null, ptr, utf8.length);
+ UNSAFE.putByte(ptr + utf8.length, (byte) 0);
+ return ptr;
+ }
+
+ static long javaStringToNativeUtf16(String s) {
+ byte[] utf16 = s.getBytes(StandardCharsets.UTF_16LE);
+ long ptr = NativeMemory.malloc(utf16.length + Short.BYTES);
+ UNSAFE.copyMemory(utf16, UNSAFE.arrayBaseOffset(byte[].class), null, ptr, utf16.length);
+ UNSAFE.putShort(ptr + utf16.length, (short) 0);
+ return ptr;
+ }
+
+ static String zeroTerminatedUtf8ToJavaString(long ptr) {
+ int len = 0;
+ while (UNSAFE.getByte(ptr + len) != 0) {
+ len++;
+ }
+ byte[] bytes = new byte[len];
+ UNSAFE.copyMemory(null, ptr, bytes, UNSAFE.arrayBaseOffset(byte[].class), len);
+ return new String(bytes, StandardCharsets.UTF_8);
+ }
+
+ static String zeroTerminatedUtf16ToJavaString(long ptr) {
+ int len = 0;
+ while (UNSAFE.getShort(ptr + len) != 0) {
+ len += Short.BYTES;
+ }
+ byte[] bytes = new byte[len];
+ UNSAFE.copyMemory(null, ptr, bytes, UNSAFE.arrayBaseOffset(byte[].class), len);
+ return new String(bytes, StandardCharsets.UTF_16LE);
+ }
+
+ private static boolean canMultiplyWithoutOverflow(long value, int stride) {
+ assert value >= 0 : "Value must be non-negative";
+ assert stride > 0 && (stride & (stride - 1)) == 0 : "Stride must be a power of two";
+ int bitsNeeded = Integer.numberOfTrailingZeros(stride);
+ return (value >>> (63 - bitsNeeded)) == 0;
+ }
+
+ private static Unsafe initUnsafe() {
+ try {
+ // Fast path when we are trusted.
+ return Unsafe.getUnsafe();
+ } catch (SecurityException se) {
+ // Slow path when we are not trusted.
+ try {
+ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ return (Unsafe) theUnsafe.get(Unsafe.class);
+ } catch (Exception e) {
+ throw CompilerDirectives.shouldNotReachHere("exception while trying to get Unsafe", e);
+ }
+ }
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSignature.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSignature.java
new file mode 100644
index 0000000000..0de2e9c2e1
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSignature.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2025, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.runtime.nativeaccess;
+
+import java.lang.invoke.MethodHandle;
+import java.util.Arrays;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+
+public final class NativeSignature {
+ private final NativeSimpleType resType;
+ private final NativeSimpleType[] argTypes;
+
+ public static NativeSignature create(NativeSimpleType resType, NativeSimpleType... argTypes) {
+ return new NativeSignature(resType, argTypes);
+ }
+
+ NativeSignature(NativeSimpleType resType, NativeSimpleType[] argTypes) {
+ this.resType = resType;
+ this.argTypes = Arrays.copyOf(argTypes, argTypes.length);
+ }
+
+ @SuppressWarnings("unused")
+ public long createClosure(NativeContext context, String name, MethodHandle staticMethodHandle) {
+ // TODO(native-access) if logging enabled, wrap the handle in a method that logs the name
+ // and args.
+ return NativeAccessSupport.createClosure(staticMethodHandle, resType, argTypes, context.arena);
+ }
+
+ @Override
+ @TruffleBoundary
+ public String toString() {
+ StringBuilder sb = new StringBuilder("(");
+ for (int i = 0; i < argTypes.length; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(argTypes[i]);
+ }
+ sb.append("): ");
+ sb.append(resType);
+ return sb.toString();
+ }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSimpleType.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSimpleType.java
new file mode 100644
index 0000000000..c2d169efc3
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSimpleType.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2025, 2026, 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
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.oracle.graal.python.runtime.nativeaccess;
+
+/**
+ * Simple native carrier types used by native access signatures.
+ *
+ * This keeps the low-level native access layer independent from C API descriptors while still
+ * preserving the information needed to derive Java carriers and FFM layouts.
+ *
+ */
+public enum NativeSimpleType {
+ VOID,
+ SINT8,
+ SINT16,
+ SINT32,
+ SINT64,
+ FLOAT,
+ DOUBLE,
+ RAW_POINTER; // arg must be long, retval is long
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java
index e87c542dde..d778a74756 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java
@@ -25,6 +25,7 @@
*/
package com.oracle.graal.python.runtime.object;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___NEW__;
import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY;
@@ -93,9 +94,8 @@
import com.oracle.graal.python.builtins.objects.bytes.PBytes;
import com.oracle.graal.python.builtins.objects.capsule.PyCapsule;
import com.oracle.graal.python.builtins.objects.cell.PCell;
-import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr;
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
-import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers;
+import com.oracle.graal.python.builtins.objects.cext.capi.TruffleObjectNativeWrapper;
import com.oracle.graal.python.builtins.objects.cext.common.CExtContext;
import com.oracle.graal.python.builtins.objects.code.PCode;
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
@@ -272,12 +272,8 @@ public static PythonObject createPythonObject(Object cls, Shape shape) {
return new PythonObject(cls, shape);
}
- public static PythonNativeVoidPtr createNativeVoidPtr(Object obj) {
- return new PythonNativeVoidPtr(obj);
- }
-
- public static PythonNativeVoidPtr createNativeVoidPtr(Object obj, long nativePtr) {
- return new PythonNativeVoidPtr(obj, nativePtr);
+ public static TruffleObjectNativeWrapper createPythonForeignObject(PythonLanguage language, Object clazz, Object foreignObject) {
+ return new TruffleObjectNativeWrapper(clazz, PythonBuiltinClassType.ForeignObject.getInstanceShape(language), foreignObject);
}
public static SuperObject createSuperObject(PythonLanguage language) {
@@ -462,7 +458,7 @@ public static PythonClass createPythonClass(Node location, PythonLanguage langua
}
public static PMemoryView createMemoryView(PythonLanguage language, PythonContext context, BufferLifecycleManager bufferLifecycleManager, Object buffer, Object owner,
- int len, boolean readonly, int itemsize, BufferFormat format, TruffleString formatString, int ndim, Object bufPointer,
+ int len, boolean readonly, int itemsize, BufferFormat format, TruffleString formatString, int ndim, long bufPointer,
int offset, int[] shape, int[] strides, int[] suboffsets, int flags) {
PythonBuiltinClassType cls = PythonBuiltinClassType.PMemoryView;
return new PMemoryView(cls, cls.getInstanceShape(language), context, bufferLifecycleManager, buffer, owner, len, readonly, itemsize, format, formatString,
@@ -473,7 +469,7 @@ public static PMemoryView createMemoryViewForManagedObject(PythonLanguage langua
TruffleString.CodePointLengthNode lengthNode, TruffleString.CodePointAtIndexUTF32Node atIndexNode) {
PythonBuiltinClassType cls = PythonBuiltinClassType.PMemoryView;
return new PMemoryView(cls, cls.getInstanceShape(language), null, null, buffer, owner, length, readonly, itemsize,
- BufferFormat.forMemoryView(format, lengthNode, atIndexNode), format, 1, null, 0, new int[]{length / itemsize}, new int[]{itemsize}, null,
+ BufferFormat.forMemoryView(format, lengthNode, atIndexNode), format, 1, NULLPTR, 0, new int[]{length / itemsize}, new int[]{itemsize}, null,
PMemoryView.FLAG_C | PMemoryView.FLAG_FORTRAN);
}
@@ -857,7 +853,7 @@ public static PFrame createPFrame(PythonLanguage language, PFrame.Reference fram
return new PFrame(language, frameInfo, location, functionOrCode, hasCustomLocals);
}
- public static PFrame createPFrame(PythonLanguage language, Object threadState, PCode code, PythonObject globals, Object localsDict) {
+ public static PFrame createPFrame(PythonLanguage language, long threadState, PCode code, PythonObject globals, Object localsDict) {
return new PFrame(language, threadState, code, globals, localsDict);
}
@@ -1532,14 +1528,10 @@ public static DigestObject createDigestObject(PythonLanguage language, PythonBui
return DigestObject.create(type, type.getInstanceShape(language), name, digest);
}
- public static PyCapsule createCapsuleNativeName(PythonLanguage language, Object pointer, Object name) {
+ public static PyCapsule createCapsuleNativeName(PythonLanguage language, long pointer, long name) {
return createCapsule(language, new PyCapsule.CapsuleData(pointer, name));
}
- public static PyCapsule createCapsuleJavaName(PythonLanguage language, Object pointer, byte[] name) {
- return createCapsule(language, new PyCapsule.CapsuleData(pointer, new CArrayWrappers.CByteArrayWrapper(name)));
- }
-
public static PyCapsule createCapsule(PythonLanguage language, PyCapsule.CapsuleData data) {
return new PyCapsule(language, data);
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeByteSequenceStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeByteSequenceStorage.java
index 1997e82374..2b1fd66751 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeByteSequenceStorage.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeByteSequenceStorage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -40,17 +40,18 @@
*/
package com.oracle.graal.python.runtime.sequence.storage;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readByteArrayElement;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeByteArrayElement;
+
import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary;
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions;
-import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
-import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
@ExportLibrary(PythonBufferAccessLibrary.class)
public final class NativeByteSequenceStorage extends NativeSequenceStorage {
- private NativeByteSequenceStorage(Object ptr, int length, int capacity) {
+ private NativeByteSequenceStorage(long ptr, int length, int capacity) {
super(ptr, length, capacity);
}
@@ -58,7 +59,7 @@ private NativeByteSequenceStorage(Object ptr, int length, int capacity) {
* @param ownsMemory whether the memory should be freed when this object dies. Should be true
* when actually used as a sequence storage
*/
- public static NativeByteSequenceStorage create(Object ptr, int length, int capacity, boolean ownsMemory) {
+ public static NativeByteSequenceStorage create(long ptr, int length, int capacity, boolean ownsMemory) {
NativeByteSequenceStorage storage = new NativeByteSequenceStorage(ptr, length, capacity);
if (ownsMemory) {
CApiTransitions.registerNativeSequenceStorage(storage);
@@ -89,14 +90,12 @@ int getBufferLength() {
}
@ExportMessage
- byte readByte(int byteOffset,
- @Cached CStructAccess.ReadByteNode readNode) {
- return readNode.readArrayElement(getPtr(), byteOffset);
+ byte readByte(int byteOffset) {
+ return readByteArrayElement(getPtr(), byteOffset);
}
@ExportMessage
- void writeByte(int byteOffset, byte value,
- @Cached CStructAccess.WriteByteNode writeNode) {
- writeNode.writeArrayElement(getPtr(), byteOffset, value);
+ void writeByte(int byteOffset, byte value) {
+ writeByteArrayElement(getPtr(), byteOffset, value);
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeObjectSequenceStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeObjectSequenceStorage.java
index 24d0bbc016..c66dbad689 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeObjectSequenceStorage.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeObjectSequenceStorage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -44,7 +44,7 @@
public final class NativeObjectSequenceStorage extends NativeSequenceStorage {
- private NativeObjectSequenceStorage(Object ptr, int length, int capacity) {
+ private NativeObjectSequenceStorage(long ptr, int length, int capacity) {
super(ptr, length, capacity);
}
@@ -52,7 +52,7 @@ private NativeObjectSequenceStorage(Object ptr, int length, int capacity) {
* @param ownsMemory whether the memory should be freed when this object dies. Should be true
* when actually used as a sequence storage
*/
- public static NativeObjectSequenceStorage create(Object ptr, int length, int capacity, boolean ownsMemory) {
+ public static NativeObjectSequenceStorage create(long ptr, int length, int capacity, boolean ownsMemory) {
NativeObjectSequenceStorage storage = new NativeObjectSequenceStorage(ptr, length, capacity);
if (ownsMemory) {
CApiTransitions.registerNativeSequenceStorage(storage);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeSequenceStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeSequenceStorage.java
index 7261a36394..8c92dd3191 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeSequenceStorage.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/sequence/storage/NativeSequenceStorage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2026, 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
@@ -40,6 +40,8 @@
*/
package com.oracle.graal.python.runtime.sequence.storage;
+import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR;
+
import java.util.logging.Level;
import com.oracle.graal.python.PythonLanguage;
@@ -47,11 +49,8 @@
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeStorageReference;
import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.TruffleLogger;
-import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.UnsupportedMessageException;
-import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
@@ -61,7 +60,7 @@ public abstract class NativeSequenceStorage extends SequenceStorage implements T
private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NativeSequenceStorage.class);
/* native pointer object */
- private Object ptr;
+ private long ptr;
private NativeStorageReference reference;
/**
@@ -74,19 +73,21 @@ public abstract class NativeSequenceStorage extends SequenceStorage implements T
*/
private Object[] replicatedNativeReferences;
- NativeSequenceStorage(Object ptr, int length, int capacity) {
+ NativeSequenceStorage(long ptr, int length, int capacity) {
super(length, capacity);
+ assert ptr != NULLPTR;
this.ptr = ptr;
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(PythonUtils.formatJString("new %s", this));
}
}
- public final Object getPtr() {
+ public final long getPtr() {
return ptr;
}
- public final void setPtr(Object ptr) {
+ public final void setPtr(long ptr) {
+ assert ptr != NULLPTR;
if (reference != null) {
reference.setPtr(ptr);
}
@@ -136,14 +137,12 @@ public Object[] getReplicatedNativeReferences() {
}
@ExportMessage
- boolean isPointer(
- @Shared @CachedLibrary(limit = "1") InteropLibrary lib) {
- return lib.isPointer(ptr);
+ boolean isPointer() {
+ return true;
}
@ExportMessage
- long asPointer(
- @Shared @CachedLibrary(limit = "1") InteropLibrary lib) throws UnsupportedMessageException {
- return lib.asPointer(ptr);
+ long asPointer() {
+ return ptr;
}
}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java
index 1318c06068..df419f01da 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java
@@ -73,6 +73,7 @@
import com.oracle.graal.python.builtins.objects.str.PString;
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
import com.oracle.graal.python.nodes.ErrorMessages;
+import com.oracle.graal.python.nodes.PGuards;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode;
@@ -844,6 +845,10 @@ public static boolean isPrimitive(Object value) {
return value instanceof Integer || value instanceof Long || value instanceof Boolean || value instanceof Double;
}
+ public static Object normalizeNone(ConditionProfile profile, Object o) {
+ return profile.profile(PGuards.isNone(o)) ? PNone.NO_VALUE : o;
+ }
+
public static final class NodeCounterWithLimit implements NodeVisitor {
private int count;
private final int limit;
@@ -1008,6 +1013,11 @@ public static String formatPointer(Object pointer) {
return String.valueOf(pointer);
}
+ public static String formatPointer(long pointer) {
+ CompilerAsserts.neverPartOfCompilation();
+ return String.format("0x%016x", pointer);
+ }
+
public static long coerceToLong(Object allocated, InteropLibrary lib) {
if (allocated instanceof Long) {
return (long) allocated;
diff --git a/mx.graalpython/eclipse-settings/org.eclipse.jdt.core.prefs b/mx.graalpython/eclipse-settings/org.eclipse.jdt.core.prefs
index 1e928980c3..493ffca3d2 100644
--- a/mx.graalpython/eclipse-settings/org.eclipse.jdt.core.prefs
+++ b/mx.graalpython/eclipse-settings/org.eclipse.jdt.core.prefs
@@ -1,2 +1,3 @@
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py
index 1001b153c1..ffed9df2d0 100644
--- a/mx.graalpython/mx_graalpython.py
+++ b/mx.graalpython/mx_graalpython.py
@@ -704,13 +704,16 @@ def __post_init__(self):
# test leaks with Python code only
run_leak_launcher(["--code", "pass", ])
run_leak_launcher(["--repeat-and-check-size", "250", "--null-stdout", "--code", "print('hello')"])
+ has_jep_454 = mx.get_jdk().version >= mx.VersionSpec("22.0.0")
# test leaks when some C module code is involved
- run_leak_launcher(["--code", 'import _testcapi, mmap, bz2; print(memoryview(b"").nbytes)'])
+ if has_jep_454:
+ run_leak_launcher(["--code", 'import _testcapi, mmap, bz2; print(memoryview(b"").nbytes)'])
# test leaks with shared engine Python code only
run_leak_launcher(["--shared-engine", "--code", "pass"])
run_leak_launcher(["--shared-engine", "--repeat-and-check-size", "250", "--null-stdout", "--code", "print('hello')"])
# test leaks with shared engine when some C module code is involved
- run_leak_launcher(["--shared-engine", "--code", 'import _testcapi, mmap, bz2; print(memoryview(b"").nbytes)'])
+ if has_jep_454:
+ run_leak_launcher(["--shared-engine", "--code", 'import _testcapi, mmap, bz2; print(memoryview(b"").nbytes)'])
run_leak_launcher(["--shared-engine", "--code", '[10, 20]', "--python.UseNativePrimitiveStorageStrategy=true",
"--forbidden-class", "com.oracle.graal.python.runtime.sequence.storage.NativePrimitiveSequenceStorage",
"--forbidden-class", "com.oracle.graal.python.runtime.native_memory.NativePrimitiveReference"])
@@ -746,6 +749,7 @@ class GraalPythonTags(object):
unittest_cpython = 'python-unittest-cpython'
unittest_sandboxed = 'python-unittest-sandboxed'
unittest_multi = 'python-unittest-multi-context'
+ unittest_multi_sandboxed = 'python-unittest-multi-context-sandboxed'
unittest_jython = 'python-unittest-jython'
unittest_arrow = 'python-unittest-arrow-storage'
unittest_hpy = 'python-unittest-hpy'
@@ -1325,8 +1329,25 @@ def run_python_unittests(python_binary, args=None, paths=None, exclude=None, env
return result
-def run_sandboxed_tests(python_binary, report, **kwargs):
- run_python_unittests(python_binary, args=SANDBOXED_OPTIONS, report=report, **kwargs)
+def run_sandboxed_tests(python_binary, report, extra_args=None, **kwargs):
+ args = SANDBOXED_OPTIONS + (extra_args or [])
+ kwargs.setdefault("parallel", 0)
+ exclude = list(kwargs.pop("exclude", []) or [])
+ env = dict(kwargs.pop("env", os.environ.copy()) or {})
+ propagated_args = [
+ "--experimental-options=true",
+ *[arg for arg in args if arg.startswith(("--python.", "--vm.", "--experimental-options"))],
+ ]
+ env["GRAAL_PYTHON_VM_ARGS"] = "\v" + "\v".join(propagated_args)
+ exclude.append(os.path.join(_python_unittest_root(), "cpyext"))
+ exclude.append(os.path.join(_python_unittest_root(), "test_ctypes_callbacks.py"))
+ exclude.append(os.path.join(_python_unittest_root(), "test_indirect_call.py"))
+ exclude.append(os.path.join(_python_unittest_root(), "test_reparse.py"))
+ if "-multi-context" in args:
+ # GR-75097: sandboxed multi-context runs currently fail in test_class-set-attrib with
+ # an unexpected class shape mismatch; skip this combination temporarily until fixed.
+ exclude.append(os.path.join(_python_unittest_root(), "test_class-set-attrib.py"))
+ run_python_unittests(python_binary, args=args, env=env, report=report, exclude=exclude, **kwargs)
# TODO the test runner doesn't even find the tests on Darwin
if sys.platform == "darwin":
return
@@ -1348,7 +1369,7 @@ def run_sandboxed_tests(python_binary, report, **kwargs):
'test_minidom.py',
]
paths = [os.path.join(tagged_test_path, test) for test in tagged_tests]
- run_tagged_unittests(python_binary, args=SANDBOXED_OPTIONS, paths=paths, report=report, **kwargs)
+ run_tagged_unittests(python_binary, args=args, paths=paths, report=report, **kwargs)
def run_hpy_unittests(python_binary, args=None, env=None, nonZeroIsFatal=True, timeout=None, report: Union[Task, bool, None] = False):
@@ -1550,6 +1571,10 @@ def graalpython_gate_runner(_, tasks):
if task:
run_python_unittests(graalpy_standalone_jvm(), args=["-multi-context"], nonZeroIsFatal=nonZeroIsFatal, report=report())
+ with Task('GraalPython sandboxed multi-context tests', tasks, tags=[GraalPythonTags.unittest_multi_sandboxed]) as task:
+ if task:
+ run_sandboxed_tests(graalpy_standalone_jvm_enterprise(), extra_args=["-multi-context"], parallel=0, report=report())
+
with Task('GraalPython Jython emulation tests', tasks, tags=[GraalPythonTags.unittest_jython]) as task:
if task:
run_python_unittests(graalpy_standalone_jvm(), args=["--python.EmulateJython"], paths=["test_interop.py"], report=report(), nonZeroIsFatal=nonZeroIsFatal)
diff --git a/mx.graalpython/mx_graalpython_bench_param.py b/mx.graalpython/mx_graalpython_bench_param.py
index b50204fce7..82fd907484 100644
--- a/mx.graalpython/mx_graalpython_bench_param.py
+++ b/mx.graalpython/mx_graalpython_bench_param.py
@@ -185,6 +185,7 @@
'unmarshal-pyc': ITER_5 + WARMUP_2 + ['50'],
'c-member-access': ITER_5 + ['30'],
'c-list-iterating-obj': ITER_5 + ['500000'],
+ 'c-dict-iterating': ITER_5 + ['500000'],
'c-magic-bool': ITER_5 + ['1000000'],
'c-magic-iter': ITER_5 + ['500000'],
'c-arith-binop': ITER_5 + ['3'],
@@ -193,8 +194,13 @@
'c-issubtype-polymorphic': ITER_5 + ['100000'],
'c-issubtype-monorphic': ITER_5 + ['200000'],
'c-call-method': ITER_5 + ['50000'],
+ 'c-call-method-o': ITER_5 + ['50000'],
+ 'c-call-method-noargs': ITER_5 + ['50000'],
+ 'c-call-method-varargs': ITER_5 + ['50000'],
'c-call-method-int-float': ITER_5 + ['500000'],
'regexp': ITER_5 + WARMUP_2,
+ 'c-upcall': ITER_5 + [str(2**28)],
+ 'c-to-c': ITER_5 + [str(2**31 - 1)],
'startup': ITER_5 + ['50'],
'startup-imports': ITER_5 + ['10'],
}
@@ -216,6 +222,7 @@ def _pickling_benchmarks(module='pickle'):
MICRO_NATIVE_BENCHMARKS = {
'c-member-access': ITER_5 + ['10000'],
'c-list-iterating-obj': ITER_5 + ['50000000'],
+ 'c-dict-iterating': ITER_5 + ['50000000'],
'c-magic-bool': ITER_5 + ['100000000'],
'c-magic-iter': ITER_5 + ['50000000'],
'c-arith-binop': ITER_5 + ['1000'],
@@ -224,8 +231,17 @@ def _pickling_benchmarks(module='pickle'):
'c-issubtype-polymorphic': ITER_5 + ['50000000'],
'c-issubtype-monorphic': ITER_5 + ['50000000'],
'c-call-method': ITER_5 + ['5000000'],
+ 'c-call-method-o': ITER_5 + ['5000000'],
+ 'c-call-method-noargs': ITER_5 + ['5000000'],
+ 'c-call-method-varargs': ITER_5 + ['5000000'],
+ 'c-call-method-fastcall': ITER_5 + ['5000000'],
'c-call-method-int-float': ITER_5 + ['5000000'],
+ 'c-call-method-obj-args': ITER_5 + ['5000000'],
'c-instantiate-large': ITER_5 + ['1000'],
+ 'c-upcall': ITER_5 + [str(2**28)],
+ 'c-upcall-builtin-function': ITER_5 + [str(2**26)],
+ 'c-upcall-slot': ITER_5 + [str(2**20)],
+ 'c-to-c': ITER_5 + [str(2**31 - 1)],
}
@@ -288,6 +304,8 @@ def _pickling_benchmarks(module='pickle'):
MACRO_BENCHMARKS = {
'gcbench': ITER_10 + ['10'],
+ 'c-pydantic-validate': ITER_10 + ['200000'],
+ 'c-pymupdf-parse': ITER_10 + ['1'],
}
diff --git a/mx.graalpython/mx_graalpython_benchmark.py b/mx.graalpython/mx_graalpython_benchmark.py
index 50eb66b2b3..edda5bf1f6 100644
--- a/mx.graalpython/mx_graalpython_benchmark.py
+++ b/mx.graalpython/mx_graalpython_benchmark.py
@@ -800,7 +800,7 @@ def get_vm_registry(self):
@classmethod
def get_benchmark_suites(cls, benchmarks):
assert isinstance(benchmarks, dict), "benchmarks must be a dict: {suite: [path, {bench: args, ... }], ...}"
- return [cls(suite_name, suite_info[0], suite_info[1])
+ return [cls(suite_name, *suite_info)
for suite_name, suite_info in benchmarks.items()]