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 { private Map initializerMap; @@ -95,6 +110,9 @@ public Object visitVariable(VariableTree variableTree, Trees trees) { private Map signatureToArgDescriptor = new HashMap<>(); private Map argDescriptorToInitializer = new HashMap<>(); + + private Map externalFunctionSignatureToInitializer = new HashMap<>(); + private Trees trees; private String getFieldInitializer(VariableElement theField) { @@ -115,6 +133,21 @@ private String getFieldInitializer(VariableElement theField) { return argDescriptorToInitializer.get(name(theField)); } + private String getExternalFunctionSignatureInitializer(VariableElement theField) { + if (trees == null) { + return ""; + } + if (externalFunctionSignatureToInitializer.isEmpty()) { + // lazily initialize all external function signatures in a single scan + var codeScanner = new ArgDescriptorsTreeScanner(); + var tp = trees.getPath(theField.getEnclosingElement()); + codeScanner.initializerMap = externalFunctionSignatureToInitializer; + codeScanner.scan(tp, this.trees); + } + return externalFunctionSignatureToInitializer.get(name(theField)); + + } + @Override public synchronized void init(ProcessingEnvironment pe) { super.init(pe); @@ -155,11 +188,7 @@ private boolean isValidReturnType(VariableElement obj) { return !initializer.matches("ArgBehavior\\.PyObject[,)]") || initializer.contains("true"); } - private boolean isVoid(VariableElement obj) { - return getFieldInitializer(obj).contains("ArgBehavior.Void"); - } - - private static String name(VariableElement obj) { + private static String name(Element obj) { return obj.getSimpleName().toString(); } @@ -169,31 +198,78 @@ private static final class CApiBuiltinDesc { public final VariableElement[] arguments; public final VariableElement returnType; public final boolean acquireGil; + public final boolean canRaise; public final String call; public final String factory; public int id; - public CApiBuiltinDesc(Element origin, String name, VariableElement returnType, VariableElement[] arguments, boolean acquireGil, String call, String factory) { + public CApiBuiltinDesc(Element origin, String name, VariableElement returnType, VariableElement[] arguments, boolean acquireGil, boolean canRaise, String call, String factory) { this.origin = origin; this.name = name; this.returnType = returnType; this.arguments = arguments; this.acquireGil = acquireGil; + this.canRaise = canRaise; this.call = call; this.factory = factory; } } + private String capiTypeToLogicalType(VariableElement element) { + var init = getFieldInitializer(element); + if (init.contains("Void")) { + return "void"; + } else if (init.contains("Float64")) { + return "double"; + } else if (init.contains("Float32")) { + return "float"; + } else if (init.contains("Int32")) { + return "int"; + } else if (init.contains("Char16")) { + return "short"; + } else if (init.contains("Char8")) { + return "byte"; + } else if (init.contains("PyObject") || init.contains("Pointer")) { + return "pointer"; + } else { + return "long"; + } + } + + private boolean isVoid(VariableElement element) { + return capiTypeToLogicalType(element).equals("void"); + } + + private String capiTypeToErrorValue(VariableElement element) { + if (capiTypeToLogicalType(element).equals("pointer")) { + return "0"; + } else { + return "-1"; + } + } + + private String capiTypeToJavaPrimitiveType(VariableElement element) { + var type = capiTypeToLogicalType(element); + return type.equals("pointer") ? "long" : type; + } + + private String capiTypeToForeignPrimitiveType(VariableElement element) { + String type = capiTypeToJavaPrimitiveType(element); + return type.equals("void") ? "void" : ("j" + type); + } + private static String argName(int i) { return "" + (char) ('a' + i); } private static final String CAPI_BUILTIN = "com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin"; private static final String CAPI_BUILTINS = "com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltins"; + private static final String CAPI_WRAPPER_DESCRIPTOR = "com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CApiWrapperDescriptor"; + private static final String INVOKE_EXTERNAL_FUNCTION = "com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.InvokeExternalFunction"; @Override public Set getSupportedAnnotationTypes() { - return Set.of(CAPI_BUILTIN, CAPI_BUILTINS, CApiFields.class.getName(), CApiConstants.class.getName(), CApiStructs.class.getName()); + return Set.of(CAPI_BUILTIN, CAPI_BUILTINS, CApiFields.class.getName(), CApiConstants.class.getName(), CApiStructs.class.getName(), CApiExternalFunctionSignatures.class.getName()); } @Override @@ -359,11 +435,16 @@ private void addCApiBuiltins(RoundEnvironment re, List javaBuil } var ret = findValue(builtin, "ret", VariableElement.class); boolean acquireGil = findValue(builtin, "acquireGil", Boolean.class); + boolean canRaise = findValue(builtin, "canRaise", Boolean.class); + if (acquireGil && !canRaise) { + processingEnv.getMessager().printError(String.format("Invalid @CApiBuiltin %s: if acquireGil is true, canRaise must be true as well (a safepoint action may run)", name, ret)); + continue; + } String call = name(findValue(builtin, "call", VariableElement.class)); // boolean inlined = findValue(builtin, "inlined", Boolean.class); VariableElement[] args = findValues(builtin, "args", VariableElement.class).toArray(new VariableElement[0]); - if (((TypeElement) element).getQualifiedName().toString().equals("com.oracle.graal.python.builtins.objects.cext.capi.CApiFunction.Dummy")) { - additionalBuiltins.add(new CApiBuiltinDesc(element, builtinName, ret, args, acquireGil, call, null)); + if (element instanceof TypeElement te && te.getQualifiedName().toString().equals("com.oracle.graal.python.builtins.objects.cext.capi.CApiFunction.Dummy")) { + additionalBuiltins.add(new CApiBuiltinDesc(element, builtinName, ret, args, acquireGil, canRaise, call, null)); } else { if (!isValidReturnType(ret)) { processingEnv.getMessager().printError( @@ -389,8 +470,12 @@ private void addCApiBuiltins(RoundEnvironment re, List javaBuil } else { genName += "NodeGen"; } - verifyNodeClass(((TypeElement) element), builtin); - javaBuiltins.add(new CApiBuiltinDesc(element, name, ret, args, acquireGil, call, genName)); + if (element instanceof TypeElement te) { + verifyNodeClass(te, builtin); + } else { + verifyStaticMethod((ExecutableElement) element, builtin); + } + javaBuiltins.add(new CApiBuiltinDesc(element, name, ret, args, acquireGil, canRaise, call, genName)); } } } @@ -401,6 +486,14 @@ private void addCApiBuiltins(RoundEnvironment re, List javaBuil } } + private void verifyStaticMethod(ExecutableElement e, AnnotationMirror annotation) { + if (!e.getModifiers().contains(Modifier.STATIC)) { + processingEnv.getMessager().printError("CApiBuiltins must be nodes or static methods", e); + } else if (e.getParameters().size() != findValues(annotation, "args", VariableElement.class).size()) { + processingEnv.getMessager().printError("Arity mismatch between declared arguments and static method", e); + } + } + private void verifyNodeClass(TypeElement te, AnnotationMirror annotation) { var tm = te.asType(); while (tm instanceof DeclaredType dt) { @@ -538,17 +631,17 @@ private void generateCApiSource(List javaBuiltins, List updateResource("capi.gen.c.h", javaBuiltins, lines); } - private void updateResource(String name, List javaBuiltins, List lines) throws IOException { + private void updateResource(String name, List javaBuiltins, List lines, StandardLocation loc) throws IOException { var origins = javaBuiltins.stream().map((jb) -> jb.origin).toArray(Element[]::new); String oldContents = ""; String newContents = String.join(System.lineSeparator(), lines); try { - oldContents = processingEnv.getFiler().getResource(StandardLocation.NATIVE_HEADER_OUTPUT, "", name).getCharContent(true).toString(); + oldContents = processingEnv.getFiler().getResource(loc, "", name).getCharContent(true).toString(); } catch (IOException e) { // pass to regenerate } if (!oldContents.equals(newContents)) { - var file = processingEnv.getFiler().createResource(StandardLocation.NATIVE_HEADER_OUTPUT, "", name, origins); + var file = processingEnv.getFiler().createResource(loc, "", name, origins); try (var w = file.openWriter()) { w.append(newContents); } @@ -557,6 +650,10 @@ private void updateResource(String name, List javaBuiltins, Lis } } + private void updateResource(String name, List javaBuiltins, List lines) throws IOException { + updateResource(name, javaBuiltins, lines, StandardLocation.NATIVE_HEADER_OUTPUT); + } + /** * Generates the builtin specification in capi.h, which includes only the builtins implemented * in Java code. Additionally, it generates helpers for all "GraalPyPrivate_Get_" and @@ -610,32 +707,93 @@ private void generateCApiHeader(List javaBuiltins) throws IOExc updateResource("capi.gen.h", javaBuiltins, lines); } + /** + * Generates the native image config for the direct upcalls to the builtins. + */ + private void generateUpcallConfig(List javaBuiltins) throws IOException { + ArrayList lines = new ArrayList<>(); + lines.add("{"); + lines.add(" \"foreign\": {"); + lines.add(" \"directUpcalls\": ["); + + for (int i = 0; i < javaBuiltins.size(); i++) { + var builtin = javaBuiltins.get(i); + String argString = Arrays.stream(builtin.arguments).map(b -> '"' + capiTypeToForeignPrimitiveType(b) + '"').collect(Collectors.joining(", ")); + String classString = getUpcallTargetClass(builtin); + String methodString = getUpcallTargetMethod(builtin); + lines.add(" {"); + lines.add(" \"class\": \"" + classString + "\","); + lines.add(" \"method\": \"" + methodString + "\","); + lines.add(" \"returnType\": \"" + capiTypeToForeignPrimitiveType(builtin.returnType) + "\","); + lines.add(" \"parameterTypes\": [" + argString + "]"); + if (i < javaBuiltins.size() - 1) { + lines.add(" },"); + } else { + lines.add(" }"); + } + } + lines.add(" ]"); + lines.add(" }"); + lines.add("}"); + + updateResource("META-INF/native-image/com.oracle.graal.python.capi/reachability-metadata.json", javaBuiltins, lines, StandardLocation.CLASS_OUTPUT); + } + + private static boolean usesUpcallWrapper(CApiBuiltinDesc builtin) { + return builtin.origin instanceof TypeElement || builtin.canRaise; + } + + private static String getUpcallTargetClass(CApiBuiltinDesc builtin) { + if (usesUpcallWrapper(builtin)) { + return "com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltinRegistry"; + } + return ((TypeElement) builtin.origin.getEnclosingElement()).getQualifiedName().toString(); + } + + private static String getUpcallTargetMethod(CApiBuiltinDesc builtin) { + if (usesUpcallWrapper(builtin)) { + return "upcall_" + builtin.name; + } + return builtin.origin.getSimpleName().toString(); + } + /** * Generates the contents of the PythonCextBuiltinRegistry class: the list of builtins, the * CApiBuiltinNode factory function, and the slot query function. */ private void generateBuiltinRegistry(List javaBuiltins) throws IOException { ArrayList lines = new ArrayList<>(); - - lines.add("// @formatter:off"); - lines.add("// Checkstyle: stop"); - lines.add("// Generated by annotation processor: " + getClass().getName()); - lines.add("package %s".formatted("com.oracle.graal.python.builtins.modules.cext;")); - lines.add(""); - lines.add("import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltinExecutable;"); - lines.add("import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltinNode;"); - lines.add("import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath;"); - lines.add("import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;"); - lines.add(""); - lines.add("public abstract class PythonCextBuiltinRegistry {"); - lines.add(""); - lines.add(" private PythonCextBuiltinRegistry() {"); - lines.add(" // no instances"); - lines.add(" }"); + // language=java + lines.add(""" + // @formatter:off + // Checkstyle: stop + // Generated by annotation processor: CApiBuiltinsProcessor + package %s; + + import java.lang.invoke.MethodHandle; + import java.lang.invoke.MethodHandles; + import java.lang.invoke.MethodType; + + import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins; + import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltinExecutable; + import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltinNode; + import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath; + import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor; + import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins; + import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeNode; + import com.oracle.graal.python.runtime.GilNode; + import com.oracle.graal.python.runtime.exception.PException; + + public final class PythonCextBuiltinRegistry { + + private PythonCextBuiltinRegistry() { + // no instances + } + """.formatted(TARGET_PACKAGE)); for (var builtin : javaBuiltins) { String argString = Arrays.stream(builtin.arguments).map(b -> "ArgDescriptor." + b).collect(Collectors.joining(", ")); - lines.add(" public static final CApiBuiltinExecutable " + builtin.name + " = new CApiBuiltinExecutable(\"" + builtin.name + "\", CApiCallPath." + builtin.call + ", ArgDescriptor." + + lines.add(" public static final CApiBuiltinExecutable " + builtin.name + " = new CApiBuiltinExecutable(\"" + builtin.name + "\", ArgDescriptor." + builtin.returnType + ", new ArgDescriptor[]{" + argString + "}, " + builtin.acquireGil + ", " + builtin.id + ");"); } lines.add(""); @@ -651,12 +809,88 @@ private void generateBuiltinRegistry(List javaBuiltins) throws for (var builtin : javaBuiltins) { lines.add(" case " + builtin.id + ":"); - lines.add(" return " + builtin.factory + ".create();"); + if (builtin.origin instanceof ExecutableElement) { + lines.add(" throw new RuntimeException(\"Static builtins should never need a node, they are just a static method.\");"); + } else { + lines.add(" return " + builtin.factory + ".create();"); + } } lines.add(" }"); lines.add(" return null;"); lines.add(" }"); + + lines.add(""); + for (var builtin : javaBuiltins) { + lines.add(" private static final MethodHandle HANDLE_" + builtin.name + ";"); + } + lines.add(" static {"); + lines.add(" try {"); + for (var builtin : javaBuiltins) { + String argString = Arrays.stream(builtin.arguments).map(b -> capiTypeToJavaPrimitiveType(b) + ".class").collect(Collectors.joining(", ")); + String classString = usesUpcallWrapper(builtin) ? "PythonCextBuiltinRegistry" : getUpcallTargetClass(builtin); + String methodString = '"' + getUpcallTargetMethod(builtin) + '"'; + lines.add(" HANDLE_" + builtin.name + " = MethodHandles.lookup().findStatic(" + classString + ".class, " + methodString + ", MethodType.methodType(" + + capiTypeToJavaPrimitiveType(builtin.returnType) + ".class" + (builtin.arguments.length > 0 ? ", " : "") + argString + "));"); + } + lines.add(" } catch (NoSuchMethodException | IllegalAccessException e) {"); + lines.add(" throw new RuntimeException(e);"); + lines.add(" }"); + lines.add(" }"); + + lines.add(""); + lines.add(" static MethodHandle getMethodHandle(int id) {"); + lines.add(" switch (id) {"); + for (var builtin : javaBuiltins) { + lines.add(" case " + builtin.id + ":"); + lines.add(" return HANDLE_" + builtin.name + ";"); + } + lines.add(" }"); + lines.add(" return null;"); + lines.add(" }"); + + for (var builtin : javaBuiltins) { + if (builtin.origin instanceof ExecutableElement && !builtin.canRaise) { + // will be called directly + continue; + } + lines.add(""); + String argString = IntStream.range(0, builtin.arguments.length).mapToObj(i -> capiTypeToJavaPrimitiveType(builtin.arguments[i]) + " " + argName(i)).collect(Collectors.joining(", ")); + if (!(builtin.origin instanceof TypeElement) && builtin.acquireGil) { + lines.add(" @SuppressWarnings(\"try\")"); + } + lines.add(" public static " + capiTypeToJavaPrimitiveType(builtin.returnType) + " upcall_" + builtin.name + "(" + argString + ") {"); + String paramString = IntStream.range(0, builtin.arguments.length).mapToObj(i -> argName(i)).collect(Collectors.joining(", ")); + String retString = capiTypeToJavaPrimitiveType(builtin.returnType); + if (retString.equals("void")) { + retString = ""; + } else { + retString = "return (" + retString + ")"; + } + if (builtin.origin instanceof TypeElement) { + lines.add(" " + retString + builtin.name + ".getCallTarget().call(" + paramString + ");"); + } else { + if (!retString.isEmpty()) { + retString = "return "; + } + assert builtin.canRaise; + lines.add(" try {"); + lines.add(" try " + (builtin.acquireGil ? "(GilNode.UncachedAcquire gil = GilNode.uncachedAcquire()) " : "") + "{"); + lines.add(" " + retString + ((TypeElement) builtin.origin.getEnclosingElement()).getQualifiedName().toString() + "." + builtin.origin.getSimpleName().toString() + "(" + + paramString + ");"); + lines.add(" } catch (Throwable t) {"); + lines.add(" throw PythonCextBuiltins.checkThrowableBeforeNative(t, \"CApiBuiltin\", \"" + builtin.name + "\");"); + lines.add(" }"); + lines.add(" } catch (PException pe) {"); + lines.add(" TransformPExceptionToNativeNode.executeUncached(pe);"); + if (!retString.isEmpty()) { + lines.add(" return " + capiTypeToErrorValue(builtin.returnType) + ";"); + } + lines.add(" }"); + } + lines.add(" }"); + } + lines.add("}"); var origins = javaBuiltins.stream().map((jb) -> jb.origin).toArray(Element[]::new); @@ -680,38 +914,29 @@ private void generateCApiAsserts(List allBuiltins) throws IOExc var origins = allBuiltins.stream().map((jb) -> jb.origin).toArray(Element[]::new); var file = processingEnv.getFiler().createSourceFile("com.oracle.graal.python.builtins.modules.cext.PythonCApiAssertions", origins); try (var w = file.openWriter()) { + // language=java w.append(""" // @formatter:off // Checkstyle: stop package %s; import java.util.TreeSet; - import com.oracle.truffle.api.CompilerDirectives; - import com.oracle.truffle.api.interop.InteropLibrary; - import com.oracle.truffle.api.interop.UnknownIdentifierException; - import com.oracle.truffle.api.interop.UnsupportedMessageException; + import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; - public abstract class PythonCApiAssertions { + public final class PythonCApiAssertions { private PythonCApiAssertions() { // no instances } - public static boolean reallyHasMember(Object capiLibrary, String name) { - try { - InteropLibrary.getUncached().readMember(capiLibrary, name); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } catch (UnknownIdentifierException e) { - return false; - } - return true; + private static boolean reallyHasMember(NativeLibrary capiLibrary, String name) { + return capiLibrary.lookupOptionalSymbol(name) != 0L; } /** * Checks whether the expected builtins exist in the library. */ - public static boolean assertBuiltins(Object capiLibrary) { + public static boolean assertBuiltins(NativeLibrary capiLibrary) { boolean hasMember = false; TreeSet messages = new TreeSet<>(); %s @@ -719,7 +944,7 @@ public static boolean assertBuiltins(Object capiLibrary) { return messages.isEmpty(); } } - """.formatted("com.oracle.graal.python.builtins.modules.cext", String.join(System.lineSeparator(), lines))); + """.formatted(TARGET_PACKAGE, String.join(System.lineSeparator(), lines))); } } @@ -835,6 +1060,894 @@ private void checkImports(List builtins) throws IOException { } } + private static final class CApiExternalFunctionSignatureDesc { + public final VariableElement origin; + public final String name; + + String returnType; + String[] argumentTypes; + public boolean cannotRaise; + + public CApiExternalFunctionSignatureDesc(VariableElement origin, String name) { + this.origin = origin; + this.name = name; + } + } + + private record InvokeExternalFunctionDesc(ExecutableElement origin, VariableElement signature, TypeMirror returnType, List argumentTypes) { + } + + private static final String NATIVE_ACCESS_PACKAGE = "com.oracle.graal.python.runtime.nativeaccess"; + private static final String NATIVE_ACCESS_SUPPORT_CLASS_NAME = "NativeAccessSupport"; + private static final String NATIVE_ACCESS_SUPPORT_IMPL_CLASS_NAME = "NativeAccessSupportJdk22Gen"; + private static final String EXFUNC_INVOKER_PACKAGE = "com.oracle.graal.python.builtins.objects.cext.capi"; + private static final String EXFUNC_INVOKER_CLASS_NAME = "ExternalFunctionInvoker"; + + /** + * Find classes annotated with {@link #CAPI_WRAPPER_DESCRIPTOR} and methods annotated with + * {@link #INVOKE_EXTERNAL_FUNCTION} and store the extraced information in + * {@link CApiExternalFunctionWrapperDesc} and {@link InvokeExternalFunctionDesc}, respectively. + */ + private List collectExternalFunctionAndWrapperDescs(RoundEnvironment re, List wrappers) { + List wrapperDescs = new ArrayList<>(); + + // TODO: remove this as soon as the annotation 'INVOKE_EXTERNAL_FUNCTION' is accessible by + // this processor + for (var rootElement : re.getRootElements()) { + collectExternalFunctionAndWrapperDescs(rootElement, wrapperDescs, wrappers); + } + return wrapperDescs; + } + + private void collectExternalFunctionAndWrapperDescs(Element element, List wrapperDescs, List wrappers) { + AnnotationMirror invokeAnnot = findAnnotationMirror(element, INVOKE_EXTERNAL_FUNCTION); + if (invokeAnnot != null) { + assert element.getKind() == ElementKind.METHOD; + VariableElement signatureElement = findValue(invokeAnnot, "value", VariableElement.class); + TypeMirror retConversion = findValue(invokeAnnot, "retConversion", TypeMirror.class); + List argConversion = findValues(invokeAnnot, "argConversions", TypeMirror.class); + wrapperDescs.add(new InvokeExternalFunctionDesc((ExecutableElement) element, signatureElement, retConversion, argConversion)); + } + + AnnotationMirror wrapperAnnot = findAnnotationMirror(element, CAPI_WRAPPER_DESCRIPTOR); + if (wrapperAnnot != null) { + List wrapperNames = findValues(wrapperAnnot, "value", VariableElement.class); + wrappers.add(new CApiExternalFunctionWrapperDesc(element, wrapperNames)); + } + + for (Element enclosedElement : element.getEnclosedElements()) { + collectExternalFunctionAndWrapperDescs(enclosedElement, wrapperDescs, wrappers); + } + } + + /** + * Maps an {@code com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgBehavior} to + * the native Java carrier type. + */ + private String toJavaNfiType(String argDescriptor) { + // TODO: types should be inferred with: 'ArgDescriptor.behavior.nativeSimpleType' + return switch (argDescriptor) { + case "Void" -> "void"; + case "Int", "InquiryResult", "InitResult", "PrimitiveResult32" -> "int"; + case "Double" -> "double"; + case "Float" -> "float"; + case "Py_hash_t", "Py_ssize_t", "PrimitiveResult64", "INT64_T", "SIZE_T", "UINTPTR_T", "Long", "UNSIGNED_LONG", "UINT64_T", + "PyObjectReturn", "PyObject", "PyObjectTransfer", "PyObjectConstArray", "PyTypeObject", "PyThreadState", "CharPtrAsTruffleString", "IterResult", "Pointer", + "CHAR_PTR" -> + "long"; + default -> { + processingEnv.getMessager().printError(String.format("Unexpected ArgDescriptor: '%s'", argDescriptor)); + yield null; + } + }; + } + + private static String getSimpleName(TypeMirror typeMirror) { + if (typeMirror.getKind() == TypeKind.DECLARED) { + return ((DeclaredType) typeMirror).asElement().getSimpleName().toString(); + } + return typeMirror.toString(); + } + + private TypeMirror toJavaNfiType(TypeMirror typeMirror) { + if (typeMirror.getKind() == TypeKind.VOID && typeMirror.getKind().isPrimitive()) { + return typeMirror; + } + return processingEnv.getTypeUtils().getPrimitiveType(TypeKind.LONG); + } + + private static String getNativeMethodHandleVarName(String signatureName) { + return "NATIVE_METHOD_HANDLE_" + signatureName; + } + + private static String toClassLiteral(String javaType) { + return switch (javaType) { + case "void" -> "void.class"; + case "int" -> "int.class"; + case "long" -> "long.class"; + case "float" -> "float.class"; + case "double" -> "double.class"; + default -> throw new IllegalArgumentException("Unexpected Java type: " + javaType); + }; + } + + private boolean isCannotRaise(VariableElement signature) { + String externalFunctionSignatureInitializer = getExternalFunctionSignatureInitializer(signature); + int start = externalFunctionSignatureInitializer.indexOf('('); + int end = externalFunctionSignatureInitializer.lastIndexOf(')'); + String[] initArgs = externalFunctionSignatureInitializer.substring(start + 1, end).split(","); + return Boolean.parseBoolean(initArgs[0].strip()); + } + + private boolean isWrapperRootInvoke(InvokeExternalFunctionDesc wrapper) { + TypeMirror wrapperBaseRoot = processingEnv.getElementUtils().getTypeElement(EXFUNC_INVOKER_PACKAGE + ".ExternalFunctionNodes.WrapperBaseRoot").asType(); + return processingEnv.getTypeUtils().isSubtype(wrapper.origin.getEnclosingElement().asType(), wrapperBaseRoot); + } + + private static String getGeneratedExternalInvokeHelperClassName(Element clazz) { + return clazz.getSimpleName() + "Gen"; + } + + private void generateExternalFunctionInvoker(List signatures) throws IOException { + ArrayList lines = new ArrayList<>(); + + lines.add("// @formatter:off"); + lines.add("// Checkstyle: stop"); + lines.add("// Generated by annotation processor: " + getClass().getName()); + lines.add("package " + EXFUNC_INVOKER_PACKAGE + ";"); + lines.add(""); + lines.add("import java.lang.invoke.MethodHandle;"); + lines.add("import java.lang.invoke.MethodType;"); + lines.add(""); + lines.add("import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;"); + lines.add("import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;"); + lines.add("import com.oracle.graal.python.builtins.objects.function.PArguments;"); + lines.add("import " + NATIVE_ACCESS_PACKAGE + ".NativeFunctionPointer;"); + lines.add("import " + NATIVE_ACCESS_PACKAGE + ".NativeContext;"); + lines.add("import " + NATIVE_ACCESS_PACKAGE + "." + NATIVE_ACCESS_SUPPORT_CLASS_NAME + ";"); + lines.add("import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext;"); + lines.add("import com.oracle.graal.python.runtime.GilNode;"); + lines.add("import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;"); + lines.add("import com.oracle.graal.python.runtime.PythonContext;"); + lines.add("import com.oracle.graal.python.runtime.PythonContext.PythonThreadState;"); + lines.add("import com.oracle.truffle.api.CompilerDirectives;"); + lines.add("import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;"); + lines.add("import " + TRUFFLE_VIRTUAL_FRAME + ";"); + lines.add(""); + lines.add("public final class " + EXFUNC_INVOKER_CLASS_NAME + " {"); + lines.add(""); + lines.add(" private " + EXFUNC_INVOKER_CLASS_NAME + "() {"); + lines.add(" // no instances"); + lines.add(" }"); + lines.add(""); + + // resolve return and argument types and declare downcall method handles + for (CApiExternalFunctionSignatureDesc sig : signatures) { + // determine arg types and return type for signature; initializer syntax: + // 'ExternalFunctionSignature(ArgDescriptor returnValue, ArgDescriptor... arguments)' + String externalFunctionSignatureInitializer = getExternalFunctionSignatureInitializer(sig.origin); + + int start = externalFunctionSignatureInitializer.indexOf('('); + int end = externalFunctionSignatureInitializer.lastIndexOf(')'); + String[] initArgs = externalFunctionSignatureInitializer.substring(start + 1, end).split(","); + + assert !sig.cannotRaise; + sig.cannotRaise = Boolean.valueOf(initArgs[0].strip()); + assert sig.returnType == null; + sig.returnType = initArgs[1].strip(); + assert sig.argumentTypes == null; + sig.argumentTypes = Arrays.stream(initArgs).skip(2).map(String::strip).toArray(String[]::new); + } + + for (CApiExternalFunctionSignatureDesc sig : signatures) { + assert sig.returnType != null; + assert sig.argumentTypes != null; + String returnType = toJavaNfiType(sig.returnType); + String returnTypeLiteral = toClassLiteral(returnType); + List argTypes = Arrays.stream(sig.argumentTypes).map(this::toJavaNfiType).toList(); + + boolean isVoidReturn = "void".equals(returnType); + + List invokeArgs = new LinkedList<>(); + if (sig.cannotRaise) { + invokeArgs.add("CApiTiming timing"); + invokeArgs.add("NativeFunctionPointer nativeFunction"); + } else { + invokeArgs.add("VirtualFrame frame"); + invokeArgs.add("CApiTiming timing"); + invokeArgs.add("NativeContext nativeContext"); + invokeArgs.add("BoundaryCallData boundaryCallData"); + invokeArgs.add("PythonThreadState threadState"); + invokeArgs.add("NativeFunctionPointer nativeFunction"); + } + int i = 0; + for (String argType : argTypes) { + invokeArgs.add(argType + " " + argName(i++)); + } + + List cArgs = new LinkedList<>(); + i = 0; + for (int unused = 0; unused < argTypes.size(); unused++) { + cArgs.add(argName(i++)); + } + + List typedArgs = new LinkedList<>(); + i = 0; + for (String argType : argTypes) { + typedArgs.add(argType + " " + argName(i++)); + } + + List methodTypeArgs = new ArrayList<>(); + methodTypeArgs.add("long.class"); + for (String argType : argTypes) { + methodTypeArgs.add(toClassLiteral(argType)); + } + lines.add(" private static final MethodHandle " + getNativeMethodHandleVarName(sig.name) + " = NativeAccessSupport.createDowncallHandle(" + + "MethodType.methodType(" + returnTypeLiteral + ", " + String.join(", ", methodTypeArgs) + "), false);"); + + lines.add(""); + if (sig.cannotRaise) { + lines.add(" public static " + returnType + " invoke" + sig.name + "(" + String.join(", ", invokeArgs) + ") {"); + String returnStmt = isVoidReturn ? "" : "return "; + lines.add(" CApiTiming.enter();"); + lines.add(" try {"); + lines.add(" " + returnStmt + "invoke" + sig.name + "(" + + "nativeFunction.getAddress()" + + (cArgs.isEmpty() ? "" : ", " + String.join(", ", cArgs)) + ");"); + lines.add(" } catch (Throwable exception) {"); + lines.add(" throw CompilerDirectives.shouldNotReachHere(exception);"); + lines.add(" } finally {"); + lines.add(" CApiTiming.exit(timing);"); + lines.add(" }"); + lines.add(" }"); + lines.add(""); + } else { + List contextInvokeArgs = new LinkedList<>(); + contextInvokeArgs.add("VirtualFrame frame"); + contextInvokeArgs.add("CApiTiming timing"); + contextInvokeArgs.add("PythonContext context"); + contextInvokeArgs.add("NativeFunctionPointer nativeFunction"); + contextInvokeArgs.addAll(typedArgs); + + String returnStmt = isVoidReturn ? "" : "return "; + + List nullFrameInvokeArgs = new LinkedList<>(); + nullFrameInvokeArgs.add("CApiTiming timing"); + nullFrameInvokeArgs.add("PythonContext context"); + nullFrameInvokeArgs.add("NativeFunctionPointer nativeFunction"); + nullFrameInvokeArgs.addAll(typedArgs); + lines.add(""); + + lines.add(" public static " + returnType + " invoke" + sig.name + "(" + String.join(", ", invokeArgs) + ") {"); + for (int j = 0; j < argTypes.size(); j++) { + if (argTypes.get(j).contains("PyObject")) { + lines.add(" assert EnsurePythonObjectNode.doesNotNeedPromotion(" + argName(j) + ");"); + } + } + lines.add(" // If any code requested the caught exception (i.e. used 'sys.exc_info()'), we store;"); + lines.add(" // it to the context since we cannot propagate it through the native frames."); + lines.add(""); + lines.add(" Object state = BoundaryCallContext.enter(frame, threadState, boundaryCallData);"); + lines.add(" CApiTiming.enter();"); + lines.add(" try {"); + lines.add(" " + returnStmt + "invoke" + sig.name + "(" + + "nativeFunction.getAddress()" + + (cArgs.isEmpty() ? "" : ", " + String.join(", ", cArgs)) + ");"); + lines.add(" } catch (Throwable exception) {"); + lines.add(" CompilerDirectives.transferToInterpreterAndInvalidate();"); + lines.add(" GilNode.uncachedAcquire();"); + lines.add(" throw CompilerDirectives.shouldNotReachHere(exception);"); + lines.add(" } finally {"); + lines.add(" CApiTiming.exit(timing);"); + lines.add(" if (frame != null && threadState.getCaughtException() != null) {"); + lines.add(" PArguments.setException(frame, threadState.getCaughtException());"); + lines.add(" }"); + lines.add(" BoundaryCallContext.exit(frame, threadState, state);"); + lines.add(" }"); + lines.add(" }"); + lines.add(""); + } + + List rawInvokeArgs = new LinkedList<>(); + rawInvokeArgs.add("long function"); + i = 0; + for (String argType : argTypes) { + rawInvokeArgs.add(argType + " " + argName(i++)); + } + + lines.add(" @TruffleBoundary(allowInlining = true, transferToInterpreterOnException = false)"); + lines.add(" public static " + returnType + " invoke" + sig.name + "(" + String.join(", ", rawInvokeArgs) + ") throws Throwable {"); + String directArgExpr = cArgs.isEmpty() ? "function" : "function, " + String.join(", ", cArgs); + if (isVoidReturn) { + lines.add(" " + getNativeMethodHandleVarName(sig.name) + ".invokeExact(" + directArgExpr + ");"); + } else { + lines.add(" return (" + returnType + ") " + getNativeMethodHandleVarName(sig.name) + ".invokeExact(" + directArgExpr + ");"); + } + lines.add(" }"); + } + lines.add("}"); + + var origins = signatures.stream().map((sig) -> sig.origin).toArray(Element[]::new); + var file = processingEnv.getFiler().createSourceFile(EXFUNC_INVOKER_PACKAGE + "." + EXFUNC_INVOKER_CLASS_NAME, origins); + try (var w = file.openWriter()) { + w.append(String.join(System.lineSeparator(), lines)); + } + } + + private void generateNativeAccessSupport(Element[] origins) throws IOException { + if (Runtime.version().feature() < 22) { + generateDummyNativeAccessSupport(origins); + return; + } + ArrayList lines = new ArrayList<>(); + + lines.add("// @formatter:off"); + lines.add("// Checkstyle: stop"); + lines.add("// Generated by annotation processor: " + getClass().getName()); + lines.add("package " + NATIVE_ACCESS_PACKAGE + ";"); + lines.add(""); + lines.add("import java.lang.foreign.Arena;"); + lines.add("import java.lang.foreign.FunctionDescriptor;"); + lines.add("import java.lang.foreign.Linker;"); + lines.add("import java.lang.foreign.MemoryLayout;"); + lines.add("import java.lang.foreign.MemorySegment;"); + lines.add("import java.lang.foreign.SymbolLookup;"); + lines.add("import java.lang.foreign.ValueLayout;"); + lines.add("import java.lang.invoke.MethodHandle;"); + lines.add("import java.lang.invoke.MethodHandles;"); + lines.add("import java.lang.invoke.MethodType;"); + lines.add("import java.util.OptionalLong;"); + lines.add(""); + lines.add("import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere;"); + lines.add(""); + lines.add("public final class " + NATIVE_ACCESS_SUPPORT_IMPL_CLASS_NAME + " extends " + NATIVE_ACCESS_SUPPORT_CLASS_NAME + " {"); + lines.add(" private static final MethodHandle OF_ADDRESS;"); + lines.add(""); + lines.add(" static {"); + lines.add(" try {"); + lines.add(" OF_ADDRESS = MethodHandles.lookup().findStatic(MemorySegment.class, \"ofAddress\", MethodType.methodType(MemorySegment.class, long.class));"); + lines.add(" } catch (NoSuchMethodException | IllegalAccessException e) {"); + lines.add(" throw new RuntimeException(e);"); + lines.add(" }"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected Object createArenaImpl() {"); + lines.add(" return Arena.ofShared();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected void closeArenaImpl(Object arena) {"); + lines.add(" ((Arena) arena).close();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" @SuppressWarnings(\"restricted\")"); + lines.add(" protected NativeLibraryLookup libraryLookupImpl(String name, Object arena) {"); + lines.add(" SymbolLookup lookup = SymbolLookup.libraryLookup(name, (Arena) arena);"); + lines.add(" return symbolName -> lookup.find(symbolName).map(segment -> OptionalLong.of(segment.address())).orElseGet(OptionalLong::empty);"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected long lookupDefaultImpl(String name) {"); + lines.add(" return Linker.nativeLinker().defaultLookup().find(name).orElseThrow().address();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" @SuppressWarnings(\"restricted\")"); + lines.add(" protected MethodHandle createDowncallHandleImpl(MethodType methodType, boolean critical) {"); + lines.add(" FunctionDescriptor functionDescriptor = createFunctionDescriptor(methodType);"); + lines.add(" Linker.Option[] options = critical ? new Linker.Option[] { Linker.Option.critical(false) } : new Linker.Option[0];"); + lines.add(" MethodHandle methodHandle = Linker.nativeLinker().downcallHandle(functionDescriptor, options);"); + lines.add(" methodHandle = MethodHandles.filterArguments(methodHandle, 0, OF_ADDRESS);"); + lines.add(" return methodHandle.asType(methodType);"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" @SuppressWarnings(\"restricted\")"); + lines.add(" protected long createClosureImpl(MethodHandle staticMethodHandle, NativeSimpleType resType, NativeSimpleType[] argTypes, Object arena) {"); + lines.add(" FunctionDescriptor functionDescriptor = createFunctionDescriptor(resType, argTypes);"); + lines.add(" return Linker.nativeLinker().upcallStub(staticMethodHandle, functionDescriptor, (Arena) arena).address();"); + lines.add(" }"); + lines.add(""); + lines.add(" private static FunctionDescriptor createFunctionDescriptor(NativeSimpleType resType, NativeSimpleType[] argTypes) {"); + lines.add(" MemoryLayout[] argLayouts = new MemoryLayout[argTypes.length];"); + lines.add(" for (int i = 0; i < argTypes.length; i++) {"); + lines.add(" argLayouts[i] = asLayout(argTypes[i]);"); + lines.add(" }"); + lines.add(" return resType == NativeSimpleType.VOID ? FunctionDescriptor.ofVoid(argLayouts) : FunctionDescriptor.of(asLayout(resType), argLayouts);"); + lines.add(" }"); + lines.add(""); + lines.add(" private static FunctionDescriptor createFunctionDescriptor(MethodType methodType) {"); + lines.add(" Class[] parameterTypes = methodType.parameterArray();"); + lines.add(" MemoryLayout[] argLayouts = new MemoryLayout[parameterTypes.length - 1];"); + lines.add(" for (int i = 1; i < parameterTypes.length; i++) {"); + lines.add(" argLayouts[i - 1] = asLayout(parameterTypes[i]);"); + lines.add(" }"); + lines.add(" Class returnType = methodType.returnType();"); + lines.add(" return returnType == void.class ? FunctionDescriptor.ofVoid(argLayouts) : FunctionDescriptor.of(asLayout(returnType), argLayouts);"); + lines.add(" }"); + lines.add(""); + lines.add(" private static MemoryLayout asLayout(Class type) {"); + lines.add(" if (type == byte.class) {"); + lines.add(" return ValueLayout.JAVA_BYTE;"); + lines.add(" } else if (type == short.class) {"); + lines.add(" return ValueLayout.JAVA_SHORT;"); + lines.add(" } else if (type == int.class) {"); + lines.add(" return ValueLayout.JAVA_INT;"); + lines.add(" } else if (type == long.class) {"); + lines.add(" return ValueLayout.JAVA_LONG;"); + lines.add(" } else if (type == float.class) {"); + lines.add(" return ValueLayout.JAVA_FLOAT;"); + lines.add(" } else if (type == double.class) {"); + lines.add(" return ValueLayout.JAVA_DOUBLE;"); + lines.add(" }"); + lines.add(" throw shouldNotReachHere(\"Unsupported layout carrier: \" + type);"); + lines.add(" }"); + lines.add(""); + lines.add(" private static MemoryLayout asLayout(NativeSimpleType type) {"); + lines.add(" return switch (type) {"); + lines.add(" case VOID -> throw shouldNotReachHere(\"VOID has no layout\");"); + lines.add(" case SINT8 -> ValueLayout.JAVA_BYTE;"); + lines.add(" case SINT16 -> ValueLayout.JAVA_SHORT;"); + lines.add(" case SINT32 -> ValueLayout.JAVA_INT;"); + lines.add(" case SINT64 -> ValueLayout.JAVA_LONG;"); + lines.add(" case FLOAT -> ValueLayout.JAVA_FLOAT;"); + lines.add(" case DOUBLE -> ValueLayout.JAVA_DOUBLE;"); + lines.add(" case RAW_POINTER -> ValueLayout.JAVA_LONG;"); + lines.add(" };"); + lines.add(" }"); + lines.add("}"); + + var file = processingEnv.getFiler().createSourceFile(NATIVE_ACCESS_PACKAGE + "." + NATIVE_ACCESS_SUPPORT_IMPL_CLASS_NAME, origins); + try (var w = file.openWriter()) { + w.append(String.join(System.lineSeparator(), lines)); + } + } + + private void generateDummyNativeAccessSupport(Element[] origins) throws IOException { + ArrayList lines = new ArrayList<>(); + + lines.add("// @formatter:off"); + lines.add("// Checkstyle: stop"); + lines.add("// Generated by annotation processor: " + getClass().getName()); + lines.add("package " + NATIVE_ACCESS_PACKAGE + ";"); + lines.add(""); + lines.add("import java.lang.invoke.MethodHandle;"); + lines.add("import java.lang.invoke.MethodType;"); + lines.add(""); + lines.add("public final class " + NATIVE_ACCESS_SUPPORT_IMPL_CLASS_NAME + " extends " + NATIVE_ACCESS_SUPPORT_CLASS_NAME + " {"); + lines.add(" @Override"); + lines.add(" protected Object createArenaImpl() {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected void closeArenaImpl(Object arena) {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected NativeLibraryLookup libraryLookupImpl(String name, Object arena) {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected long lookupDefaultImpl(String name) {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected MethodHandle createDowncallHandleImpl(MethodType methodType, boolean critical) {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add(""); + lines.add(" @Override"); + lines.add(" protected long createClosureImpl(MethodHandle staticMethodHandle, NativeSimpleType resType, NativeSimpleType[] argTypes, Object arena) {"); + lines.add(" throw unsupported();"); + lines.add(" }"); + lines.add("}"); + + var file = processingEnv.getFiler().createSourceFile(NATIVE_ACCESS_PACKAGE + "." + NATIVE_ACCESS_SUPPORT_IMPL_CLASS_NAME, origins); + try (var w = file.openWriter()) { + w.append(String.join(System.lineSeparator(), lines)); + } + } + + /** + * @param wrapperNames enum constant names denoting wrapper descriptors + */ + private record CApiExternalFunctionWrapperDesc(Element origin, List wrapperNames) { + } + + private boolean verifyArguments(ExecutableElement origin, String... expectedPrefixArgs) { + if (origin.getParameters().size() < expectedPrefixArgs.length) { + processingEnv.getMessager().printError(String.format("Method \"%s\" must at least have parameters %s.", origin, Arrays.toString(expectedPrefixArgs)), origin); + return false; + } + assert origin.getParameters().size() >= expectedPrefixArgs.length; + Iterator iterator = origin.getParameters().iterator(); + for (int i = 0; i < expectedPrefixArgs.length; i++) { + VariableElement formalParameter = iterator.next(); + TypeElement te = (TypeElement) processingEnv.getTypeUtils().asElement(formalParameter.asType()); + + if (!expectedPrefixArgs[i].equals(te.getQualifiedName().toString())) { + processingEnv.getMessager().printError(String.format("Argument %d of method \"%s\" must be of type \"%s\" but was \"%s\"", i, origin, expectedPrefixArgs[i], formalParameter), origin); + return false; + } + } + return true; + } + + private static boolean needsConversion(TypeMirror type) { + return !type.getKind().isPrimitive() && type.getKind() != TypeKind.VOID; + } + + private static boolean needsReachabilityFence(TypeMirror type) { + return !type.getKind().isPrimitive() && type.getKind() != TypeKind.NULL; + } + + /** + * Lookup a method in {@code enclosingType} that exactly satisfies the specified signature and + * has the given name prefix. + */ + private ExecutableElement findMethod(Element location, TypeMirror enclosingType, String prefix, TypeMirror retType, TypeMirror... argTypes) { + Types typeUtils = processingEnv.getTypeUtils(); + Elements elementUtils = processingEnv.getElementUtils(); + + Element el = typeUtils.asElement(enclosingType); + if (!(el instanceof TypeElement typeElement)) { + processingEnv.getMessager().printError("Type %s does not have any executable methods.", location); + // Not a declared type (could be primitive, array, type variable, etc.) + return null; + } + + for (Element e : elementUtils.getAllMembers(typeElement)) { + if (e.getKind() == ElementKind.METHOD && e.getSimpleName().toString().startsWith(prefix)) { + ExecutableElement method = (ExecutableElement) e; + // if (retType.equals(method.getReturnType()) && + // argType.equals(method.getParameters().get(0).asType()) ) { + TypeMirror[] parameters = method.getParameters().stream().map(VariableElement::asType).toArray(TypeMirror[]::new); + if (retType.equals(method.getReturnType()) && Arrays.equals(parameters, argTypes)) { + return method; + } + } + } + processingEnv.getMessager().printError(String.format("Type %s does not have any \"%s %s*(%s)\" method.", + enclosingType, retType, prefix, Arrays.stream(argTypes).map(Object::toString).collect(Collectors.joining(", "))), location); + return null; + } + + private static final String WRAPPER_DESCRIPTOR_GEN_CLASS_NAME = "WrapperDescriptorRootNodesGen"; + + private void generateExternalFunctionHelperNodes(List externalFunctionDescs) throws IOException { + final Types typeUtils = processingEnv.getTypeUtils(); + final Elements elementUtils = processingEnv.getElementUtils(); + TypeMirror nodeType = elementUtils.getTypeElement(TRUFFLE_NODE).asType(); + Map> helperInvokeDescsByClass = new LinkedHashMap<>(); + + for (InvokeExternalFunctionDesc desc : externalFunctionDescs) { + if (!isWrapperRootInvoke(desc)) { + TypeElement clazz = (TypeElement) desc.origin.getEnclosingElement(); + helperInvokeDescsByClass.computeIfAbsent(clazz, k -> new ArrayList<>()).add(desc); + } + } + + for (Map.Entry> entry : helperInvokeDescsByClass.entrySet()) { + TypeElement clazz = entry.getKey(); + if (!typeUtils.isSubtype(clazz.asType(), nodeType)) { + processingEnv.getMessager().printError(String.format("Type %s must extend %s.", clazz, TRUFFLE_NODE), clazz); + return; + } + + String packageName = elementUtils.getPackageOf(clazz).getQualifiedName().toString(); + String genClassName = getGeneratedExternalInvokeHelperClassName(clazz); + ArrayList lines = new ArrayList<>(); + + lines.add("// @formatter:off"); + lines.add("// Checkstyle: stop"); + lines.add("// Generated by annotation processor: " + getClass().getName()); + lines.add("package " + packageName + ";"); + lines.add(""); + lines.add("import " + clazz.getQualifiedName() + ";"); + lines.add("import " + EXFUNC_INVOKER_PACKAGE + "." + EXFUNC_INVOKER_CLASS_NAME + ";"); + lines.add("import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;"); + lines.add("import " + NATIVE_FUNCTION_POINTER + ";"); + lines.add("import " + PYTHON_CONTEXT + ";"); + lines.add("import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;"); + lines.add("import " + TRUFFLE_VIRTUAL_FRAME + ";"); + lines.add(""); + lines.add("public final class " + genClassName + " extends " + clazz.getSimpleName() + " {"); + lines.add(""); + lines.add(" private static final " + genClassName + " UNCACHED = new " + genClassName + "();"); + lines.add(""); + for (InvokeExternalFunctionDesc helper : entry.getValue()) { + lines.add(" private static final CApiTiming TIMING_" + helper.origin.getSimpleName() + " = CApiTiming.create(true, \"" + helper.origin.getSimpleName() + "\");"); + } + lines.add(""); + lines.add(" private " + genClassName + "() {"); + lines.add(" }"); + lines.add(""); + lines.add(" public static " + clazz.getSimpleName() + " create() {"); + lines.add(" return new " + genClassName + "();"); + lines.add(" }"); + lines.add(""); + lines.add(" public static " + clazz.getSimpleName() + " getUncached() {"); + lines.add(" return UNCACHED;"); + lines.add(" }"); + + for (InvokeExternalFunctionDesc helper : entry.getValue()) { + List formalParameters = helper.origin.getParameters(); + boolean isVoidReturn = helper.origin.getReturnType().getKind() == TypeKind.VOID; + boolean cannotRaise = isCannotRaise(helper.signature); + + if (!verifyArguments(helper.origin, TRUFFLE_VIRTUAL_FRAME, PYTHON_CONTEXT, NATIVE_FUNCTION_POINTER)) { + return; + } + + int actualArgConversionClasses = helper.argumentTypes.size(); + int expectedArgConversionClasses = formalParameters.size() - 3; + if (actualArgConversionClasses != expectedArgConversionClasses) { + processingEnv.getMessager().printError(String.format("You need to specify exactly %d argument conversion classes but there were %d.", + expectedArgConversionClasses, actualArgConversionClasses), helper.origin); + return; + } + + List methodInvokeFormalArgs = new ArrayList<>(formalParameters.size()); + List cArgs = new ArrayList<>(); + if (cannotRaise) { + cArgs.add("TIMING_" + helper.origin.getSimpleName()); + cArgs.add(formalParameters.get(2).getSimpleName().toString()); // nativeFunction + } else { + cArgs.add(formalParameters.get(0).getSimpleName().toString()); // frame + cArgs.add("TIMING_" + helper.origin.getSimpleName()); + cArgs.add(formalParameters.get(1).getSimpleName().toString()); // context + cArgs.add(formalParameters.get(2).getSimpleName().toString()); // nativeFunction + } + + for (VariableElement formalParameter : formalParameters) { + TypeMirror type = formalParameter.asType(); + Element element = typeUtils.asElement(type); + String typeString = element != null ? element.getSimpleName().toString() : type.toString(); + methodInvokeFormalArgs.add(typeString + " " + formalParameter.getSimpleName()); + } + for (int i = 3; i < formalParameters.size(); i++) { + cArgs.add(formalParameters.get(i).getSimpleName().toString()); + } + + lines.add(""); + lines.add(" @Override"); + lines.add(" protected " + getSimpleName(helper.origin.getReturnType()) + " " + helper.origin.getSimpleName() + "(" + String.join(", ", methodInvokeFormalArgs) + ") {"); + if (isVoidReturn) { + lines.add(" " + EXFUNC_INVOKER_CLASS_NAME + ".invoke" + helper.signature + "(" + String.join(", ", cArgs) + ");"); + } else { + lines.add(" return " + EXFUNC_INVOKER_CLASS_NAME + ".invoke" + helper.signature + "(" + String.join(", ", cArgs) + ");"); + } + lines.add(" }"); + } + + lines.add("}"); + + var origins = entry.getValue().stream().map((desc) -> desc.origin).toArray(Element[]::new); + var file = processingEnv.getFiler().createSourceFile(packageName + "." + genClassName, origins); + try (var w = file.openWriter()) { + w.append(String.join(System.lineSeparator(), lines)); + } + } + } + + private void generateExternalFunctionRootNodes(List externalFunctionDescs, List wrappers, + @SuppressWarnings("unused") Map externalFunctionSignatures) throws IOException { + final Types typeUtils = processingEnv.getTypeUtils(); + List wrapperInvokeDescs = externalFunctionDescs.stream().filter(this::isWrapperRootInvoke).toList(); + ArrayList lines = new ArrayList<>(); + + lines.add("// @formatter:off"); + lines.add("// Checkstyle: stop"); + lines.add("// Generated by annotation processor: " + getClass().getName()); + lines.add("package " + EXFUNC_INVOKER_PACKAGE + ";"); + lines.add(""); + + lines.add("import com.oracle.graal.python.PythonLanguage;"); + lines.add("import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;"); + for (InvokeExternalFunctionDesc wrapper : wrapperInvokeDescs) { + Element clazz = wrapper.origin.getEnclosingElement(); + lines.add("import " + EXFUNC_INVOKER_PACKAGE + "." + clazz.getEnclosingElement().getSimpleName() + "." + clazz.getSimpleName() + ";"); + } + lines.add("import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;"); + lines.add("import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.WrapperDescriptorRoot;"); + lines.add("import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;"); + lines.add("import " + NATIVE_FUNCTION_POINTER + ";"); + lines.add("import com.oracle.graal.python.runtime.ExecutionContext.CalleeContext;"); + lines.add("import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;"); + lines.add("import com.oracle.graal.python.runtime.PythonContext;"); + lines.add("import com.oracle.graal.python.runtime.PythonContext.GetThreadStateNode;"); + lines.add("import com.oracle.truffle.api.CompilerDirectives;"); + lines.add("import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;"); + lines.add("import " + TRUFFLE_VIRTUAL_FRAME + ";"); + lines.add("import com.oracle.truffle.api.nodes.Node.Child;"); + lines.add("import com.oracle.truffle.api.strings.TruffleString;"); + lines.add(""); + int importMarker = lines.size(); + lines.add(""); + lines.add("import java.lang.ref.Reference;"); + lines.add(""); + lines.add("public final class " + WRAPPER_DESCRIPTOR_GEN_CLASS_NAME + " {"); + lines.add(""); + lines.add(" private " + WRAPPER_DESCRIPTOR_GEN_CLASS_NAME + "() {"); + lines.add(" // no instances"); + lines.add(" }"); + lines.add(""); + + Set classesToImport = new HashSet<>(); + + // declare downcall signature variables and resolve return and argument types + for (InvokeExternalFunctionDesc wrapper : wrapperInvokeDescs) { + boolean errorOccurred = false; + boolean cannotRaise = isCannotRaise(wrapper.signature); + + assert wrapper.returnType != null; + assert wrapper.argumentTypes != null; + List formalParameters = wrapper.origin.getParameters(); + + boolean isVoidReturn = wrapper.origin.getReturnType().getKind() == TypeKind.VOID; + + // verify arguments of annotated method + if (!verifyArguments(wrapper.origin, TRUFFLE_VIRTUAL_FRAME, NATIVE_FUNCTION_POINTER)) { + return; + } + + // check if the right count of argument conversion classes was specified + int actualArgConversionClasses = wrapper.argumentTypes.size(); + int expectedArgConversionClasses = formalParameters.size() - 2; + if (actualArgConversionClasses != expectedArgConversionClasses) { + processingEnv.getMessager().printError(String.format("You need to specify exactly %d argument conversion classes but there were %d.", + expectedArgConversionClasses, actualArgConversionClasses), wrapper.origin); + return; + } + + // formal arguments of method 'invokeExternalFunction' + List methodInvokeFormalArgs = new ArrayList<>(formalParameters.size()); + List needReachabilityFence = new LinkedList<>(); + for (VariableElement formalParameter : formalParameters) { + TypeMirror type = formalParameter.asType(); + Element element = typeUtils.asElement(type); + String typeString; + if (element != null) { + typeString = element.getSimpleName().toString(); + } else { + typeString = type.toString(); + } + methodInvokeFormalArgs.add(typeString + " " + formalParameter.getSimpleName()); + } + + // list of arguments passed to 'ExternalFunctionInvoker.invoke*' method + List cArgs = new LinkedList<>(); + if (cannotRaise) { + cArgs.add("timing"); + cArgs.add(formalParameters.get(1).getSimpleName().toString()); // nativeFunction + } else { + cArgs.add(formalParameters.getFirst().getSimpleName().toString()); // frame + cArgs.add("timing"); + cArgs.add("context.ensureNativeContext()"); + cArgs.add("boundaryCallData"); // boundaryCallData + cArgs.add("getThreadStateNode.executeCached(context)"); // threadState + cArgs.add(formalParameters.get(1).getSimpleName().toString()); // nativeFunction + } + + Element clazz = wrapper.origin.getEnclosingElement(); + String genClassName = clazz.getSimpleName() + "Gen"; + + lines.add(""); + lines.add(" public static final class " + genClassName + " extends " + clazz.getSimpleName() + " {"); + lines.add(""); + lines.add(" @Child private CalleeContext calleeContext = CalleeContext.create();"); + lines.add(" @Child private BoundaryCallData boundaryCallData;"); + lines.add(" @Child private GetThreadStateNode getThreadStateNode = GetThreadStateNode.create();"); + for (int j = 0; j < wrapper.argumentTypes.size(); j++) { + TypeMirror argConversionClass = wrapper.argumentTypes.get(j); + + VariableElement formalParameter = formalParameters.get(j + 2); + Name formalParameterName = formalParameter.getSimpleName(); + String convertNodeName = "convert" + formalParameterName; + if (needsConversion(argConversionClass)) { + TypeElement argConversionClassTypeElement = (TypeElement) typeUtils.asElement(argConversionClass); + classesToImport.add(argConversionClassTypeElement.getQualifiedName()); + Name simpleName = argConversionClassTypeElement.getSimpleName(); + ExecutableElement createMethod = findMethod(wrapper.origin, argConversionClass, "create", argConversionClass); + String createMethodName = createMethod != null ? createMethod.getSimpleName().toString() : "null"; + lines.add(String.format(" @Child private %s %s = %s.%s();", simpleName, convertNodeName, simpleName, createMethodName)); + + /* + * TODO: Reliably determine the expected type for the actual parameter. + * + * This is about the required type for the actual parameter for the call of + * `ExternalFunctionInvoker.invoke*`. The required parameter type should be + * inferred from the formal parameter type of the invoke method. Since those + * methods are generated in the same go, we cannot rely on them being already + * available. However, we know how they are generated in + * `generateExternalFunctionInvoker` and we could use that. For this, we will + * need table `externalFunctionSignatures`. + */ + TypeMirror expectedActualType = toJavaNfiType(formalParameter.asType()); + + ExecutableElement executeMethod = findMethod(wrapper.origin, argConversionClass, "execute", expectedActualType, formalParameter.asType()); + String executeMethodName = executeMethod != null ? executeMethod.getSimpleName().toString() : "null"; + if (executeMethod != null) { + /* + * Also already generate the expression that invokes the Python-to-native + * conversion node. + */ + cArgs.add(String.format("%s.%s(%s)", convertNodeName, executeMethodName, formalParameterName)); + } else { + errorOccurred = true; + } + } else { + // If no conversion is required, then just pass the formal parameter. + cArgs.add(formalParameterName.toString()); + } + + if (needsReachabilityFence(formalParameter.asType())) { + needReachabilityFence.add(formalParameter); + } + } + lines.add(""); + lines.add(" private final CApiTiming timing;"); + lines.add(""); + lines.add(" public " + genClassName + "(PythonLanguage language, TruffleString name, PExternalFunctionWrapper wrapper) {"); + lines.add(" super(language, name, wrapper);"); + lines.add(" this.timing = CApiTiming.create(true, name);"); + lines.add(" this.boundaryCallData = BoundaryCallData.createFor(this);"); + lines.add(" }"); + lines.add(""); + + lines.add(" @Override"); + lines.add(" protected " + getSimpleName(wrapper.origin.getReturnType()) + " invokeExternalFunction(" + String.join(", ", methodInvokeFormalArgs) + ") {"); + lines.add(" PythonContext context = PythonContext.get(this);"); + + String returnStmt = isVoidReturn ? "" : "return "; + lines.add(" try {"); + if (errorOccurred) { + lines.add(" throw new RuntimeException(\"error occurred during generation\");"); + } else { + lines.add(" " + returnStmt + EXFUNC_INVOKER_CLASS_NAME + "." + "invoke" + wrapper.signature + "(" + String.join(", ", cArgs) + ");"); + } + lines.add(" } finally {"); + for (VariableElement formalParameter : needReachabilityFence) { + lines.add(String.format(" Reference.reachabilityFence(%s);", formalParameter.getSimpleName())); + } + lines.add(" }"); + + lines.add(" }"); + lines.add(" }"); + } + + for (Name classToImport : classesToImport) { + lines.add(importMarker++, String.format("import %s;", classToImport)); + } + + // generate factory method + lines.add(" @TruffleBoundary"); + lines.add(" public static WrapperDescriptorRoot create(PythonLanguage language, TruffleString name, PExternalFunctionWrapper wrapper) {"); + lines.add(" return switch (wrapper) {"); + for (CApiExternalFunctionWrapperDesc wrapper : wrappers) { + String wrapperList = wrapper.wrapperNames.stream().map(CApiBuiltinsProcessor::name).collect(Collectors.joining(", ")); + lines.add(" case " + wrapperList + " -> new " + name(wrapper.origin) + "Gen(language, name, wrapper);"); + } + lines.add(" default -> throw CompilerDirectives.shouldNotReachHere(\"no root node for wrapper \" + wrapper);"); + lines.add(" };"); + lines.add(" }"); + + // closing brace for WRAPPER_DESCRIPTOR_GEN_CLASS_NAME + lines.add("}"); + + var origins = wrapperInvokeDescs.stream().map((desc) -> desc.origin).toArray(Element[]::new); + var file = processingEnv.getFiler().createSourceFile(EXFUNC_INVOKER_PACKAGE + "." + WRAPPER_DESCRIPTOR_GEN_CLASS_NAME, origins); + try (var w = file.openWriter()) { + w.append(String.join(System.lineSeparator(), lines)); + } + } + @Override @SuppressWarnings({"try", "unused"}) public boolean process(Set annotations, RoundEnvironment re) { @@ -856,11 +1969,12 @@ public boolean process(Set annotations, RoundEnvironment allBuiltins.add(entry); } } - Collections.sort(allBuiltins, (a, b) -> a.name.compareTo(b.name)); + allBuiltins.sort((a, b) -> a.name.compareTo(b.name)); List constants = new ArrayList<>(); List fields = new ArrayList<>(); List structs = new ArrayList<>(); + List externalFunctionSignatures = new ArrayList<>(); for (var el : re.getElementsAnnotatedWith(CApiConstants.class)) { if (el.getKind() == ElementKind.ENUM) { for (var enumBit : el.getEnclosedElements()) { @@ -874,7 +1988,7 @@ public boolean process(Set annotations, RoundEnvironment } for (var el : re.getElementsAnnotatedWith(CApiFields.class)) { if (el.getKind() != ElementKind.ENUM) { - processingEnv.getMessager().printError(CApiConstants.class.getSimpleName() + " is only applicable for enums.", el); + processingEnv.getMessager().printError(CApiFields.class.getSimpleName() + " is only applicable for enums.", el); } else { for (var enumBit : el.getEnclosedElements()) { if (enumBit.getKind() == ElementKind.ENUM_CONSTANT) { @@ -885,7 +1999,7 @@ public boolean process(Set annotations, RoundEnvironment } for (var el : re.getElementsAnnotatedWith(CApiStructs.class)) { if (el.getKind() != ElementKind.ENUM) { - processingEnv.getMessager().printError(CApiConstants.class.getSimpleName() + " is only applicable for enums.", el); + processingEnv.getMessager().printError(CApiStructs.class.getSimpleName() + " is only applicable for enums.", el); } else { for (var enumBit : el.getEnclosedElements()) { if (enumBit.getKind() == ElementKind.ENUM_CONSTANT) { @@ -894,17 +2008,37 @@ public boolean process(Set annotations, RoundEnvironment } } } + Map sigs = new HashMap<>(); + for (var el : re.getElementsAnnotatedWith(CApiExternalFunctionSignatures.class)) { + if (el.getKind() == ElementKind.ENUM) { + for (var enumBit : el.getEnclosedElements()) { + if (enumBit.getKind() == ElementKind.ENUM_CONSTANT) { + CApiExternalFunctionSignatureDesc value = new CApiExternalFunctionSignatureDesc((VariableElement) enumBit, enumBit.getSimpleName().toString()); + sigs.put(value.name, value); + } + } + } else { + processingEnv.getMessager().printError(CApiExternalFunctionSignatures.class.getSimpleName() + " is only applicable for enums.", el); + } + } + List cApiExternalFunctionWrapperDescs = new LinkedList<>(); + List externalFunctionDescs = collectExternalFunctionAndWrapperDescs(re, cApiExternalFunctionWrapperDescs); if (allBuiltins.isEmpty()) { return true; } try { + generateNativeAccessSupport(allBuiltins.stream().map((builtin) -> builtin.origin).toArray(Element[]::new)); if (trees != null) { // needs jdk.compiler generateCApiSource(allBuiltins, constants, fields, structs); generateCApiHeader(javaBuiltins); + generateExternalFunctionInvoker(new ArrayList<>(sigs.values())); + generateExternalFunctionHelperNodes(externalFunctionDescs); + generateExternalFunctionRootNodes(externalFunctionDescs, cApiExternalFunctionWrapperDescs, sigs); } generateBuiltinRegistry(javaBuiltins); + generateUpcallConfig(javaBuiltins); generateCApiAsserts(allBuiltins); if (trees != null) { // needs jdk.compiler diff --git a/graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java b/graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java index dc8fa4f152..77f3f3cb7e 100644 --- a/graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java +++ b/graalpython/com.oracle.graal.python.shell/src/com/oracle/graal/python/shell/GraalPythonMain.java @@ -274,7 +274,8 @@ protected List preprocessArguments(List givenArgs, Map pythonThreads() { @Test public void testNoNewThreadsWithoutAutomaticAsyncActions() { Assume.assumeTrue("false".equalsIgnoreCase(System.getProperty("python.AutomaticAsyncActions"))); + Assume.assumeTrue("Requires JEP 454 support for _testcapi", Runtime.version().feature() >= 22); long threadCount = pythonThreadCount(); Context c = PythonTests.enterContext(Map.of("python.AllowSignalHandlers", "true", "python.PosixModuleBackend", "native"), new String[0]); try { diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/NativeExtTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/NativeExtTest.java index dcaf0dcb23..c9322f6b0b 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/NativeExtTest.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/NativeExtTest.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 @@ -58,6 +58,7 @@ public class NativeExtTest { @BeforeClass public static void setUpClass() { Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("mac")); + Assume.assumeTrue(Runtime.version().feature() >= 22); } @Test diff --git a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/ShutdownTest.java b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/ShutdownTest.java index 05a7caca42..b7cd9025f8 100644 --- a/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/ShutdownTest.java +++ b/graalpython/com.oracle.graal.python.test.integration/src/com/oracle/graal/python/test/integration/advanced/ShutdownTest.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 @@ -59,6 +59,7 @@ public class ShutdownTest extends PythonTests { @BeforeClass public static void setUpClass() { Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("mac")); + Assume.assumeTrue(Runtime.version().feature() >= 22); } @Test diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/cext/test/MultithreadedImportTestJava.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/cext/test/MultithreadedImportTestJava.java index 5ad07a58da..87649f163f 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/cext/test/MultithreadedImportTestJava.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/cext/test/MultithreadedImportTestJava.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, 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,9 +43,16 @@ import static com.oracle.graal.python.cext.test.MultithreadedImportTestBase.multithreadedImportTest; import org.graalvm.polyglot.Context; +import org.junit.Assume; +import org.junit.BeforeClass; import org.junit.Test; public class MultithreadedImportTestJava { + @BeforeClass + public static void setUpClass() { + Assume.assumeTrue(Runtime.version().feature() >= 22); + } + @Test public void testImportOnMultipleThreads() { Context context = Context.newBuilder().allowAllAccess(true).option("python.PosixModuleBackend", "java").build(); diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/cext/test/MultithreadedImportTestNative.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/cext/test/MultithreadedImportTestNative.java index 6e5c54af4a..0eb9ee8f69 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/cext/test/MultithreadedImportTestNative.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/cext/test/MultithreadedImportTestNative.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, 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 @@ -41,9 +41,16 @@ package com.oracle.graal.python.cext.test; import org.graalvm.polyglot.Context; +import org.junit.Assume; +import org.junit.BeforeClass; import org.junit.Test; public class MultithreadedImportTestNative extends MultithreadedImportTestBase { + @BeforeClass + public static void setUpClass() { + Assume.assumeTrue(Runtime.version().feature() >= 22); + } + @Test public void testImportOnMultipleThreads() { Context context = Context.newBuilder().allowAllAccess(true).option("python.PosixModuleBackend", "native").build(); diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java index 15edba3d06..f188f9974c 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.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,7 +42,11 @@ import java.util.EnumSet; +import org.junit.After; import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -51,16 +55,34 @@ import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotMeta; 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.runtime.nativeaccess.NativeContext; +import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer; +import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType; import com.oracle.graal.python.util.Function; public class TpSlotsTests { + private NativeContext nativeContext; + + @BeforeClass + public static void setUpClass() { + Assume.assumeTrue(Runtime.version().feature() >= 22); + } + + @Before + public void setUp() { + nativeContext = NativeContext.create(); + } + + @After + public void tearDown() { + nativeContext.close(); + } + @Test public void testBuilderBasic() { Builder builder = TpSlots.newBuilder(); for (TpSlotMeta def : TpSlotMeta.VALUES) { - // Use the TpSlotMeta as dummy "callable" object to verify that the slot values were - // properly assigned to the right fields of TpSlots record - builder.set(def, TpSlotNative.createCExtSlot(def)); + builder.set(def, createCExtSlot(def)); } TpSlots slots = builder.build(); @@ -95,9 +117,9 @@ public void testBuilderExplicitGroup() { @Test public void testBuilderOptimizations1() { Builder builder = TpSlots.newBuilder(); - builder.set(TpSlotMeta.MP_LENGTH, TpSlotNative.createCExtSlot(TpSlotMeta.MP_LENGTH)); - builder.set(TpSlotMeta.TP_GETATTR, TpSlotNative.createCExtSlot(TpSlotMeta.TP_GETATTR)); - builder.set(TpSlotMeta.TP_SETATTR, TpSlotNative.createCExtSlot(TpSlotMeta.TP_SETATTR)); + builder.set(TpSlotMeta.MP_LENGTH, createCExtSlot(TpSlotMeta.MP_LENGTH)); + builder.set(TpSlotMeta.TP_GETATTR, createCExtSlot(TpSlotMeta.TP_GETATTR)); + builder.set(TpSlotMeta.TP_SETATTR, createCExtSlot(TpSlotMeta.TP_SETATTR)); TpSlots slots = builder.build(); verifySlots(slots, def -> def == TpSlotMeta.MP_LENGTH || def == TpSlotMeta.TP_GETATTR || def == TpSlotMeta.TP_SETATTR); @@ -111,7 +133,7 @@ public void testBuilderOptimizations1() { @Test public void testBuilderOptimizations2() { Builder builder = TpSlots.newBuilder(); - builder.set(TpSlotMeta.SQ_LENGTH, TpSlotNative.createCExtSlot(TpSlotMeta.SQ_LENGTH)); + builder.set(TpSlotMeta.SQ_LENGTH, createCExtSlot(TpSlotMeta.SQ_LENGTH)); TpSlots slots = builder.build(); verifySlots(slots, def -> def == TpSlotMeta.SQ_LENGTH); @@ -137,8 +159,16 @@ private static void verifySlots(TpSlots slots, Function che } } + // Use the TpSlotMeta's ordinal value as a pointer for creating a dummy native function pointer + // to + // verify that the slot values were properly assigned to the right fields of TpSlots + // record + private TpSlotNative createCExtSlot(TpSlotMeta def) { + return TpSlotNative.createCExtSlot(NativeFunctionPointer.create(nativeContext, def.ordinal(), NativeSimpleType.VOID)); + } + private static void checkSlotValue(TpSlotMeta def, TpSlot slotValue) { - Assert.assertTrue(def.name(), slotValue instanceof TpSlotNative slotNative && slotNative.getCallable() == def); + Assert.assertTrue(def.name(), slotValue instanceof TpSlotNative slotNative && slotNative.getCallable().getAddress() == def.ordinal()); } private static boolean getGroup(TpSlots slots, TpSlotGroup group) { diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/CExtContextTest.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/CExtContextTest.java index 8e3778ceee..5988f5c1bb 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/CExtContextTest.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/CExtContextTest.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 org.junit.Test; import com.oracle.graal.python.builtins.objects.cext.common.CExtContext; +import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.test.PythonTests; import com.oracle.truffle.api.strings.TruffleString; @@ -80,7 +81,7 @@ public void testGetBaseName() { } private static class TestCExtContext extends CExtContext { - public TestCExtContext(PythonContext context, Object library) { + public TestCExtContext(PythonContext context, NativeLibrary library) { super(context, library, null); } diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/SlotWrapperTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/SlotWrapperTests.java index fb0c70913d..13e633f01a 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/SlotWrapperTests.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/cext/SlotWrapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2024, 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,11 +45,20 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +import java.util.Map; + +import org.junit.After; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; -import com.oracle.graal.python.builtins.objects.cext.capi.PyProcsWrapper.TpSlotWrapper; +import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext; +import com.oracle.graal.python.builtins.objects.cext.capi.TpSlotWrapper; import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotMeta; import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotPythonSingle; +import com.oracle.graal.python.runtime.GilNode; +import com.oracle.graal.python.test.PythonTests; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.strings.TruffleString; @@ -58,6 +67,27 @@ public class SlotWrapperTests { private static final Object DUMMY_CALLABLE = new Object(); private static final Object DUMMY_TYPE = new Object(); + private GilNode.UncachedAcquire gil; + + @BeforeClass + public static void setUpClass() { + Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("mac")); + Assume.assumeTrue(Runtime.version().feature() >= 22); + } + + @Before + public void setUp() { + PythonTests.enterContext(Map.of("python.IsolateNativeModules", "true"), new String[0]); + gil = GilNode.uncachedAcquire(); + CApiContext.ensureCapiWasLoaded("test slot wrapper"); + } + + @After + public void tearDown() { + gil.close(); + PythonTests.closeContext(); + } + @Test public void testCloneContract() { TruffleString testName = PythonUtils.toTruffleStringUncached("__test__"); diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/nodes/MemMoveNodeTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/nodes/MemMoveNodeTests.java index b479cb41f1..c472150be6 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/nodes/MemMoveNodeTests.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/nodes/MemMoveNodeTests.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 @@ -68,6 +68,7 @@ public class MemMoveNodeTests { @BeforeClass public static void setUpClass() { Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("mac")); + Assume.assumeTrue(Runtime.version().feature() >= 22); } @Before diff --git a/graalpython/com.oracle.graal.python.test/src/tests/__init__.py b/graalpython/com.oracle.graal.python.test/src/tests/__init__.py index a1ea48b477..40cd1e0e9f 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/__init__.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/__init__.py @@ -60,34 +60,60 @@ def find_rootdir(): DIR = find_rootdir() -def get_setuptools(setuptools='setuptools==67.6.1'): - """ - distutils is not part of std library since python 3.12 - we rely on distutils to pick the toolchain for the underlying system - and build the c extension tests. - """ - import site - setuptools_path = find_rootdir() / ('%s-setuptools-venv' % sys.implementation.name) +def _venv_site_packages(venv_dir: Path) -> Path: + if sys.platform.startswith('win32'): + return venv_dir / 'Lib' / 'site-packages' + return venv_dir / 'lib' / f'python{sys.version_info.major}.{sys.version_info.minor}' / 'site-packages' + + +def _package_present(site_packages_dir: Path, package: str, version: str) -> bool: + if os.path.isdir(site_packages_dir / f'{package}-{version}.dist-info'): + return True + normalized = package.replace('-', '_') + return os.path.isdir(site_packages_dir / normalized) + - if not os.path.isdir(setuptools_path / 'setuptools'): +def ensure_packages(**package_specs): + import site + package_names = "-".join(package_specs.keys()) + venv_dir = find_rootdir() / f'{sys.implementation.name}-{package_names}-venv' + site_packages_dir = _venv_site_packages(venv_dir) + if any(not _package_present(site_packages_dir, p, v) for p, v in package_specs.items()): import subprocess - print('installing setuptools in %s' % setuptools_path) - system_python = install_venv(setuptools_path) + package_specs = [f'{p}=={v}' for p, v in package_specs.items()] + print(f'installing {package_specs} in {venv_dir}') + system_python = install_venv(venv_dir) if sys.platform.startswith('win32'): - py_executable = setuptools_path / 'Scripts' / 'python.exe' + py_executable = venv_dir / 'Scripts' / 'python.exe' + else: + py_executable = venv_dir / 'bin' / 'python3' + extra_args = [] + if system_python or sys.implementation.name != "graalpy": + pass + elif __graalpython__.is_bytecode_dsl_interpreter: + extra_args = ['--vm.Dpython.EnableBytecodeDSLInterpreter=true'] else: - py_executable = setuptools_path / 'bin' / 'python3' - subprocess.run([py_executable, "-m", "pip", "install", "--target", str(setuptools_path), setuptools], check=True) - print('setuptools is installed in %s' % setuptools_path) + extra_args = ['--vm.Dpython.EnableBytecodeDSLInterpreter=false'] + subprocess.run([py_executable, *extra_args, "-m", "pip", "install", *package_specs], check=True) + print(f'{package_specs} installed in {venv_dir}') - pyvenv_site = str(setuptools_path) + pyvenv_site = str(site_packages_dir) if os.path.normcase(os.path.normpath(pyvenv_site)) not in {os.path.normcase(os.path.normpath(entry)) for entry in sys.path}: site.addsitedir(pyvenv_site) +def get_setuptools(setuptools='67.6.1'): + """ + distutils is not part of std library since python 3.12 + we rely on distutils to pick the toolchain for the underlying system + and build the c extension tests. + """ + ensure_packages(setuptools=setuptools) + + def install_venv(venv_path: Path) -> bool: """Installs a virtual environment at the given path.""" - if not sys.executable: + if not sys.executable or (sys.platform.startswith('win32') and sys.implementation.name == "graalpy"): # When running in a PolyBench benchmark context sys.executable is unset # And thus we must defer to the system's python # Deferring to the system's python is fine as it will only be used to install setuptools diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/demo2.c b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/demo2.c index ef54928760..01ea97cf46 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/demo2.c +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/demo2.c @@ -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 @@ -52,7 +52,7 @@ static PyObject* demo_system(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "s", &command)) { return NULL; } - retval = (char*)calloc(sizeof(char), strlen(command) + strlen(ANSWER)); + retval = (char*)calloc(sizeof(char), strlen(command) + strlen(ANSWER) + 1); sprintf(retval, "%s%s", command, ANSWER); return Py_BuildValue("s", retval); } diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ceval.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ceval.py index b3a785697b..c95e9aa6ec 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ceval.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_ceval.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2024, 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 @@ -47,7 +47,8 @@ class TestCeval(CPyExtTestCase): lambda args: None if args[0] < 800 else RecursionError(args[1]), lambda: ( (1, ": one"), - (500, ": five hundred"), + # C_RECURSION_LIMIT is very small on CPython debug builds + (400, ": four hundred"), (10000, ": ten thousand"), ), code=""" diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_dict.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_dict.py index 17bd48ff22..772a0baa6c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_dict.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_dict.py @@ -1,4 +1,4 @@ -# 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 @@ -307,7 +307,7 @@ class TestPyDict(CPyExtTestCase): # PyDict_Next test_PyDict_Next = CPyExtFunctionOutVars( _reference_next, - lambda: (({'a': "hello"}, 1), ({'a': "hello"}, 0), ({'a': "hello", 'b': 'world'}, 1), ({'a': "hello"}, 1)), + lambda: (({'a': "hello"}, 1), ({'a': "hello"}, 0), ({'a': "hello", 'b': 'world'}, 1), ({'a': "hello"}, 1), ({True: False}, 0)), code='''int wrap_PyDict_Next(PyObject* dict, Py_ssize_t* ppos, PyObject** key, PyObject** value) { int res = 0; Py_ssize_t iterations = *ppos; diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_gc.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_gc.py index c6bfef2ce5..4adf3e869e 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_gc.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_gc.py @@ -1,4 +1,4 @@ -# 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 @@ -120,6 +120,99 @@ tp_methods='{"getCounters", (PyCFunction)getCounters, METH_NOARGS | METH_STATIC, ""}, {"resetCounters", (PyCFunction)resetCounters, METH_NOARGS | METH_STATIC, ""}', ) +GCTestDeallocUpcall = CPyExtType("GCTestDeallocUpcall", + ''' + #include + + static PyObject *gtd_callback = NULL; + + #if GRAALVM_PYTHON + extern PyAPI_FUNC(int) (*GraalPyPrivate_DisableReferenceQueuePolling)(void); + extern PyAPI_FUNC(void) (*GraalPyPrivate_EnableReferenceQueuePolling)(void); + extern PyAPI_FUNC(void) (*GraalPyPrivate_TriggerGC)(size_t); + #endif + + static GCTestDeallocUpcallObject* gtd_new_instance(PyTypeObject* cls) { + return PyObject_New(GCTestDeallocUpcallObject, cls); + } + + static int gtd_init(GCTestDeallocUpcallObject* self, PyObject* args, PyObject* kwargs) { + return 0; + } + + static void gtd_dealloc(GCTestDeallocUpcallObject* self) { + PyObject *result; + + Py_INCREF((PyObject*) self); + if (gtd_callback != NULL) { + result = PyObject_CallFunctionObjArgs(gtd_callback, (PyObject*) self, NULL); + if (result == NULL) { + PyErr_WriteUnraisable(gtd_callback); + } else { + Py_DECREF(result); + } + } + /* On GraalPy, here is: 'Py_REFCNT(self) > MANAGED_REFCNT'. + * Don't use Py_DECREF to avoid recursive dealloc on CPython. + */ + Py_SET_REFCNT((PyObject*) self, Py_REFCNT(self) - 1); + Py_TYPE(self)->tp_free((PyObject*) self); + } + + static PyObject* gtd_create_decref_and_reuse(PyObject* cls, PyObject* unused) { + PyTypeObject* type = (PyTypeObject*) cls; + GCTestDeallocUpcallObject* original = NULL; + #if GRAALVM_PYTHON + int polling_disabled = 0; + #endif + + #if GRAALVM_PYTHON + if (GraalPyPrivate_DisableReferenceQueuePolling()) { + PyErr_SetString(PyExc_RuntimeError, "reference queue polling is already active"); + return NULL; + } + polling_disabled = 1; + #endif + + original = gtd_new_instance(type); + if (original == NULL) { + goto error; + } + Py_DECREF((PyObject*) original); + #if GRAALVM_PYTHON + GraalPyPrivate_EnableReferenceQueuePolling(); + polling_disabled = 0; + GraalPyPrivate_TriggerGC(0); + #endif + Py_RETURN_NONE; + + error: + #if GRAALVM_PYTHON + if (polling_disabled) { + GraalPyPrivate_EnableReferenceQueuePolling(); + } + #endif + return NULL; + } + + static PyObject* gtd_set_callback(PyObject* cls, PyObject* arg) { + if (arg == Py_None) { + Py_CLEAR(gtd_callback); + } else { + Py_XSETREF(gtd_callback, Py_NewRef(arg)); + } + Py_RETURN_NONE; + } + + ''', + tp_init='(initproc)gtd_init', + tp_methods=""" + {"set_callback", (PyCFunction)gtd_set_callback, METH_O | METH_STATIC, ""}, + {"create_decref_and_reuse", (PyCFunction)gtd_create_decref_and_reuse, METH_NOARGS | METH_CLASS, ""} + """, + tp_dealloc='(destructor)gtd_dealloc', +) + class TestGC1(unittest.TestCase): def test_native_class(self): @@ -222,6 +315,22 @@ def _trigger_gc(self): time.sleep(0.25) gc.collect() + def test_dealloc_upcall_with_temporary_refcnt_does_not_double_dealloc(self): + seen = [] + + def dealloc_callback(obj): + seen.append(type(obj).__name__) + # This dealloc_callback must not keep 'obj' alive past 'type(obj).__name__' + if GRAALPY: + assert not __graalpython__.has_id_reference(obj) + return None + + GCTestDeallocUpcall.set_callback(dealloc_callback) + GCTestDeallocUpcall.create_decref_and_reuse() + assert seen == ["GCTestDeallocUpcall"] + self._trigger_gc() + GCTestDeallocUpcall.set_callback(None) + def test_cycle_with_native_objects(self): TestCycle0 = CPyExtType("TestCycle0", ''' diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_list.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_list.py index 74f3e0a587..98b30b217c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_list.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_list.py @@ -1,4 +1,4 @@ -# 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 @@ -312,9 +312,7 @@ class TestPyList(CPyExtTestCase): ([None],), ([],), ([1,2,3,4],), - # no type checking, also accepts different objects - ((1,2,3,4,5),), - ({"a": 1, "b":2},), + # no type checking, must not use non-list objects ), resultspec="n", argspec='O', diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_method.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_method.py index 559175a1b8..8402861a3b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_method.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_method.py @@ -1,4 +1,4 @@ -# 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 @@ -38,6 +38,8 @@ # SOFTWARE. import types import unittest +import gc +import time from . import CPyExtType, CPyExtTestCase, unhandled_error_compare, CPyExtFunction, assert_raises @@ -170,6 +172,126 @@ class TestMethodsSubclass(TestMethods): assert_raises(TypeError, obj.meth_static_fastcall, 1, 2, a=1) + def test_meth_varargs_with_escaping_args_tuple(self): + TestEscapingArgsTuple = CPyExtType( + "TestEscapingArgsTuple", + """ + static int init(PyObject *selfObj, PyObject *args, PyObject *kwargs) { + TestEscapingArgsTupleObject *self = (TestEscapingArgsTupleObject *) selfObj; + if (PyArg_ParseTuple(args, "OO", &self->container, &self->appender) == 0) { + return -1; + } + Py_INCREF(self->container); + self->object = NULL; + return 0; + } + + static int force_native_storage(PyObject *args) { + PyObject *first = PyTuple_GET_ITEM(args, 0); + if (!first) { + PyErr_SetString(PyExc_ValueError, "first item must not be null"); + return -1; + } + return 0; + } + + static PyObject* hold(TestEscapingArgsTupleObject *self, PyObject *args) { + if (force_native_storage(args) == -1) { + return NULL; + } + Py_XSETREF(self->object, Py_NewRef(args)); + Py_RETURN_NONE; + } + + static PyObject* steal(TestEscapingArgsTupleObject *self, PyObject *args) { + if (force_native_storage(args) == -1) { + return NULL; + } + Py_INCREF(args); + PyList_SetItem(self->container, 0, args); + Py_RETURN_NONE; + } + + static PyObject* give_to_managed(TestEscapingArgsTupleObject *self, PyObject *args) { + if (force_native_storage(args) == -1) { + return NULL; + } + self->stolen = args; + self->stolen_element = PyTuple_GET_ITEM(args, 0); + return PyObject_CallOneArg(self->appender, args); + } + + static PyObject* recursive(TestEscapingArgsTupleObject *self, PyObject *args) { + if (force_native_storage(args) == -1) { + return NULL; + } + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + if (nargs > 1) { + return PyLong_FromSsize_t(nargs); + } + return PyObject_CallNoArgs(PyTuple_GET_ITEM(args, 0)); + } + """, + cmembers=""" + PyObject *container; + PyObject *appender; + PyObject *object; + PyObject *stolen; + PyObject *stolen_element; + """, + tp_methods=""" + {"hold", _PyCFunction_CAST(hold), METH_VARARGS, ""}, + {"steal", _PyCFunction_CAST(steal), METH_VARARGS, ""}, + {"give_to_managed", _PyCFunction_CAST(give_to_managed), METH_VARARGS, ""}, + {"recursive", _PyCFunction_CAST(recursive), METH_VARARGS, ""} + """, + tp_members=''' + {"object", T_OBJECT, offsetof(TestEscapingArgsTupleObject, object), 0, NULL}, + {"stolen", T_OBJECT, offsetof(TestEscapingArgsTupleObject, stolen), 0, NULL}, + {"stolen_element", T_OBJECT, offsetof(TestEscapingArgsTupleObject, stolen_element), 0, NULL} + ''', + tp_basicsize="sizeof(TestEscapingArgsTupleObject)", + tp_new="PyType_GenericNew", + tp_init="init", + ) + + appender_list = [] + def append_to_list(item): + appender_list.append(item) + return None + + def recursive_call(): + return tester.recursive('x', 'y', 'z') + + container = [None] + tester = TestEscapingArgsTuple(container, append_to_list) + + # the first time, the args tuple will be passed with a managed storage + tester.hold("hello", "world") + tester.steal(1, 2, 3) + assert container[0] == (1, 2, 3) + tester.give_to_managed('a', 'b', 'c') + assert appender_list[0] == ('a', 'b', 'c') + recursive_result = tester.recursive(recursive_call) + assert recursive_result == 3 + + # the second time, the args tuple will be passed with native storage + tester.hold("hello", "beautiful", "world") + tester.steal(4, 5, 6) + tester.give_to_managed('d', 'e', 'f') + + for _ in range(3): + gc.collect() + time.sleep(0.5) + + assert tester.object == ("hello", "beautiful", "world") + assert container[0] == (4, 5, 6) + assert appender_list[1] == ('d', 'e', 'f') + assert tester.stolen == ('d', 'e', 'f') + assert tester.stolen_element == 'd' + assert tester.stolen[0] is tester.stolen_element + + class TestPyMethod(CPyExtTestCase): test_PyMethod_New = CPyExtFunction( diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_thread.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_thread.py index bd314eb931..56d23fe9f9 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_thread.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_thread.py @@ -1,4 +1,4 @@ -# 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 @@ -82,7 +82,8 @@ class TestPyThread(CPyExtTestCase): ) -@unittest.skipIf(sys.platform == 'win32', "Needs pthread") +# TODO(native-access) support ENV - thread attach/detach +@unittest.skipIf(True, "Needs pthread") class TestNativeThread(unittest.TestCase): def test_register_new_thread(self): TestThread = CPyExtType( diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tp_slots.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tp_slots.py index 84d348fae8..a20ccd0acf 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tp_slots.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tp_slots.py @@ -1,4 +1,4 @@ -# 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 @@ -1471,6 +1471,10 @@ def __init__(self, delegate): assert next(obj) == 1 assert_raises(StopIteration, next, obj) + for obj in [NativeSlotProxy(iter([1])), NativeSlotProxy(PureSlotProxy(iter([1])))]: + assert obj.__next__() == 1 + assert_raises(StopIteration, obj.__next__) + def test_tp_iternext_not_implemented(): class ManagedTypeWithVanishingNext: diff --git a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py index 8442ad72a9..c91d5015bc 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_tuple.py @@ -126,9 +126,7 @@ class TestPyTuple(CPyExtTestCase): ((1, 2, 3),), (("a", "b"),), (TupleSubclass(1, 2, 3),), - # no type checking, also accepts different objects - ([1, 2, 3, 4],), - ({"a": 1, "b":2},), + # no type checking, must not use non-tuple objects ), resultspec="n", argspec='O', diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_array.py b/graalpython/com.oracle.graal.python.test/src/tests/test_array.py index a3b58d0107..362a1e2a45 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_array.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_array.py @@ -41,7 +41,7 @@ import types from array import array -from tests.util import storage_to_native +from tests.util import skip_if_sandboxed, storage_to_native def assert_raises(err, fn, *args, **kwargs): @@ -103,6 +103,7 @@ def test_add_int_to_long_array(): assert y[0] == 42 +@skip_if_sandboxed("Needs native storage support in sandboxed runs") def test_array_native_storage(): a = array('l', [1, 2, 3]) storage_to_native(a) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py b/graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py index 3c8d784d9f..7a0b33167c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_bytes.py @@ -1,4 +1,4 @@ -# 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,7 +40,7 @@ import sys import unittest -from tests.util import storage_to_native +from tests.util import skip_if_sandboxed, storage_to_native def assert_raises(err, fn, *args, **kwargs): @@ -99,6 +99,7 @@ def test_constructor_value_errors(): # assert_raises(ValueError, bytes, [10**100]) +@skip_if_sandboxed("Needs native storage support in sandboxed runs") def test_reverse(): b = bytearray(b'hello') assert b.reverse() is None diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_ctypes.py b/graalpython/com.oracle.graal.python.test/src/tests/test_ctypes.py index 761e047f15..ff44710f02 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_ctypes.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_ctypes.py @@ -1,4 +1,4 @@ -# 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 @@ -46,6 +46,7 @@ from pathlib import Path from tests.testlib_helper import build_testlib +from tests.util import skip_if_sandboxed class TestCtypesInterop(unittest.TestCase): @@ -74,6 +75,7 @@ def run_in_subprocess(self, code, *args): ) ) + @skip_if_sandboxed("Needs native extension support for ctypes in sandboxed runs") def test_ctypes_load_and_call(self): # Pass the library path as an argument code = textwrap.dedent( @@ -91,6 +93,7 @@ def test_ctypes_load_and_call(self): self.run_in_subprocess(code, str(self.lib_path)) @unittest.skipIf(sys.platform != "win32", "Windows-only test") + @skip_if_sandboxed("Needs native extension support for ctypes in sandboxed runs") def test_os_add_dll_directory_and_unload(self): # Pass the library dir as argument code = textwrap.dedent( diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_exception.py b/graalpython/com.oracle.graal.python.test/src/tests/test_exception.py index 2e576e13d1..dfa1802d9e 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_exception.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_exception.py @@ -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-2017 Python Software Foundation # # Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -7,6 +7,8 @@ import sys import errno +from tests.util import skip_if_sandboxed + GRAALPYTHON = sys.implementation.name == "graalpy" def fun0(test_obj, expected_error): @@ -678,6 +680,7 @@ def __getattr__(self, i): raise AttributeError1 @unittest.skipUnless(GRAALPYTHON, "There is no simple way to restrict memory for CPython process") +@skip_if_sandboxed("Sandboxed runs use a restricted runtime configuration for subprocess memory tests") def test_memory_error(): import subprocess compiler_options = [] diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_list.py b/graalpython/com.oracle.graal.python.test/src/tests/test_list.py index f81a919fbb..9bfbeaf88c 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_list.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_list.py @@ -8,7 +8,7 @@ from tests import list_tests from tests.compare import CompareTest -from tests.util import storage_to_native +from tests.util import skip_if_sandboxed, storage_to_native LONG_NUMBER = 6227020800 @@ -79,6 +79,10 @@ def test_len(self): self.assertEqual(len([0]), 1) self.assertEqual(len([0, 1, 2]), 3) + @skip_if_sandboxed("Needs native storage support in sandboxed runs") + def test_reverse(self): + super().test_reverse() + def test_overflow(self): lst = [4, 5, 6, 7] n = int((sys.maxsize * 2 + 2) // len(lst)) @@ -835,6 +839,7 @@ def test_generalize_store(self): l += [0x100000000, 'a'] self.assertEqual([1, 0x100000000, 'a'], l) + @skip_if_sandboxed("Needs native storage support in sandboxed runs") def test_reverse(self): l = [1, 2, 3] self.assertEqual(None, l.reverse()) @@ -866,6 +871,7 @@ def __repr__(self): return self.name +@skip_if_sandboxed("Needs native storage support in sandboxed runs") class NativeStorageTests(unittest.TestCase): def setUp(self): self.o1 = TestObject('o1') diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_multiprocessing_graalpy.py b/graalpython/com.oracle.graal.python.test/src/tests/test_multiprocessing_graalpy.py index 81a9cb0fac..033071c400 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_multiprocessing_graalpy.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_multiprocessing_graalpy.py @@ -45,6 +45,8 @@ import sys import time +from tests.util import skip_if_sandboxed + if sys.implementation.name == 'graalpy': def graalpy_multiprocessing(test): @@ -71,6 +73,7 @@ def test_SemLock_raises_on_non_string_name(): @graalpy_multiprocessing + @skip_if_sandboxed("Sandboxed runs use an emulated backend for multiprocessing wait") def test_wait_timeout(): timeout = 3 a, b = multiprocessing.Pipe() @@ -87,6 +90,7 @@ def test_wait_timeout(): @graalpy_multiprocessing + @skip_if_sandboxed("Sandboxed runs use an emulated backend for multiprocessing wait") def test_wait(): a, b = multiprocessing.Pipe() x, y = multiprocessing.connection.Pipe(False) # Truffle multiprocessing pipe diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_sqlite3.py b/graalpython/com.oracle.graal.python.test/src/tests/test_sqlite3.py index 11ee8b0689..21ae70d3bb 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_sqlite3.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_sqlite3.py @@ -1,4 +1,4 @@ -# 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 @@ -37,10 +37,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from tests.util import skip_if_sandboxed + + +@skip_if_sandboxed("Needs native extension support for sqlite3 in sandboxed runs") def test_basic_functionality(): """ This is a basic test to ensure that the module can be imported. - The main sqlite3 test suite will be silently skipped if the + The main sqlite3 test suite will be silently skipped if the "_sqlite3" module is not available. """ import sqlite3 @@ -51,6 +55,7 @@ def test_basic_functionality(): conn.close() +@skip_if_sandboxed("Needs native extension support for sqlite3 in sandboxed runs") def test_fts5_works(): # we explicitly enable those features below, but on CPython they might not # be available if using some system libsqlite that doesn't have them diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_venv.py b/graalpython/com.oracle.graal.python.test/src/tests/test_venv.py index e37d7a2e5a..dcde552f60 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_venv.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_venv.py @@ -45,6 +45,8 @@ import textwrap import unittest +from tests.util import _is_sandboxed + BINDIR = 'bin' if sys.platform != 'win32' else 'Scripts' EXESUF = '' if sys.platform != 'win32' else '.exe' @@ -173,6 +175,8 @@ def test_create_and_use_basic_venv(self): assert self.env_dir in run, run def test_create_and_use_venv_with_pip(self): + if sys.platform == "win32" and _is_sandboxed(): + self.skipTest("Skipped in sandboxed configuration on Windows due to pip relying on winreg/ctypes lookup during ensurepip") run = None msg = '' try: diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_wheel.py b/graalpython/com.oracle.graal.python.test/src/tests/test_wheel.py index d07ee2dfa3..f27271477a 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_wheel.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_wheel.py @@ -1,4 +1,4 @@ -# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. +# 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 @@ -44,6 +44,8 @@ import sys import tempfile import unittest +import importlib +import importlib.machinery from pathlib import Path from tests.testlib_helper import build_testlib @@ -142,6 +144,30 @@ def test_build_install_and_run(self): self.assertEqual(result, "42") + @unittest.skipUnless(sys.platform == "win32", "Windows-specific native loader regression test") + def test_bad_native_module_reports_import_error(self): + with tempfile.TemporaryDirectory(prefix="testwheel_badext_") as tmpdir: + tmpdir = Path(tmpdir) + module_name = "badext" + badext = tmpdir / f"{module_name}{importlib.machinery.EXTENSION_SUFFIXES[0]}" + badext.write_bytes(b"") + + sys.path.insert(0, str(tmpdir)) + importlib.invalidate_caches() + sys.modules.pop(module_name, None) + try: + with self.assertRaises(ImportError) as ctx: + importlib.import_module(module_name) + finally: + sys.path.pop(0) + sys.modules.pop(module_name, None) + + message = str(ctx.exception) + self.assertIn("cannot load", message) + self.assertIn("Windows error 193", message) + self.assertNotIn("ShouldNotReachHere", message) + self.assertNotIn("CompilerDirectives", message) + if __name__ == "__main__": unittest.main() diff --git a/graalpython/com.oracle.graal.python.test/src/tests/util.py b/graalpython/com.oracle.graal.python.test/src/tests/util.py index b28238c01b..f47e5efbcb 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/util.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/util.py @@ -1,4 +1,4 @@ -# 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,6 +41,24 @@ IS_BYTECODE_DSL = sys.implementation.name == 'graalpy' and __graalpython__.is_bytecode_dsl_interpreter + +def _is_sandboxed(): + return ( + sys.implementation.name == 'graalpy' and + __graalpython__.posix_module_backend() == 'java' and + __graalpython__.sha3_module_backend() == 'java' and + __graalpython__.pyexpat_module_backend() == 'java' + ) + + +def skip_if_sandboxed(reason=''): + def wrapper(test): + if _is_sandboxed(): + return unittest.skip(f"Skipped in sandboxed configuration. {reason}")(test) + return test + return wrapper + + def skipIfBytecodeDSL(reason=''): def wrapper(test): if IS_BYTECODE_DSL: diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java index 38c7969fce..9fc3341abe 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java @@ -62,6 +62,7 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.ImpModuleBuiltins; import com.oracle.graal.python.builtins.modules.SignalModuleBuiltins; +import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltinRegistry; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PNotImplemented; import com.oracle.graal.python.builtins.objects.PythonAbstractObject; @@ -324,6 +325,7 @@ public boolean isSingleContext() { private final ConcurrentHashMap runtimeCachedCallTargets = new ConcurrentHashMap<>(); @CompilationFinal(dimensions = 1) private final RootCallTarget[] builtinSlotsCallTargets; + @CompilationFinal(dimensions = 1) private RootCallTarget[] capiCallTargets; /** * We cannot initialize call targets in language ctor and the next suitable hook is context @@ -335,7 +337,7 @@ public boolean isSingleContext() { private final Shape emptyShape = Shape.newBuilder().allowImplicitCastIntToDouble(false).allowImplicitCastIntToLong(true).shapeFlags(0).propertyAssumptions(true).build(); @CompilationFinal(dimensions = 1) private final Shape[] builtinTypeInstanceShapes = new Shape[PythonBuiltinClassType.VALUES.length]; - @CompilationFinal(dimensions = 1) public static final PythonAbstractObject[] CONTEXT_INSENSITIVE_SINGLETONS = new PythonAbstractObject[]{PNone.NONE, PNone.NO_VALUE, PEllipsis.INSTANCE, + @CompilationFinal(dimensions = 1) public static final PythonAbstractObject[] CONTEXT_INSENSITIVE_SINGLETONS = new PythonAbstractObject[]{PNone.NONE, PEllipsis.INSTANCE, PNotImplemented.NOT_IMPLEMENTED}; /** @@ -1074,6 +1076,24 @@ public void setBuiltinSlotCallTarget(int index, RootCallTarget callTarget) { builtinSlotsCallTargets[index] = callTarget; } + public RootCallTarget getCapiCallTarget(int index) { + if (capiCallTargets == null) { + return null; + } + return capiCallTargets[index]; + } + + public void setCapiCallTarget(int index, RootCallTarget ct) { + CompilerAsserts.neverPartOfCompilation(); + if (capiCallTargets == null) { + RootCallTarget[] callTargets = new RootCallTarget[PythonCextBuiltinRegistry.builtins.length]; + VarHandle.storeStoreFence(); + capiCallTargets = callTargets; + } + VarHandle.storeStoreFence(); + capiCallTargets[index] = ct; + } + /** * Caches call target that wraps a node that is not parametrized, i.e., has only a parameterless * ctor and all its instances implement the same logic. Parametrized nodes must include the @@ -1091,6 +1111,10 @@ public RootCallTarget createCachedCallTarget(Function return createCachedCallTargetUnsafe(rootNodeFunction, key, true); } + public RootCallTarget createCachedCallTarget(Function rootNodeFunction, int key) { + return createCachedCallTargetUnsafe(rootNodeFunction, key, true); + } + public RootCallTarget createCachedCallTarget(Function rootNodeFunction, Class nodeClass, String key) { // for builtins: name is needed to distinguish builtins that share the same underlying node // in general: a String may be parameter of the node wrapped in the root node or the root diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/CodecsModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/CodecsModuleBuiltins.java index 68fde3ee71..23bf5fb2ef 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/CodecsModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/CodecsModuleBuiltins.java @@ -728,6 +728,11 @@ protected ArgumentClinicProvider getArgumentClinic() { public abstract static class PyCodecLookupNode extends PNodeWithContext { public abstract PTuple execute(Frame frame, Node inliningTarget, TruffleString encoding); + @TruffleBoundary + public static PTuple executeUncached(TruffleString encoding) { + return CodecsModuleBuiltinsFactory.PyCodecLookupNodeGen.getUncached().execute(null, null, encoding); + } + @Specialization static PTuple lookup(VirtualFrame frame, Node inliningTarget, TruffleString encoding, @Cached CodecsRegistry.EnsureRegistryInitializedNode ensureRegistryInitializedNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GcModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GcModuleBuiltins.java index 63eb16d9eb..72c68d5540 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GcModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GcModuleBuiltins.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) 2013, Regents of the University of California * * All rights reserved. @@ -38,11 +38,11 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.GcModuleBuiltinsClinicProviders.GcCollectNodeClinicProviderGen; import com.oracle.graal.python.builtins.modules.GcModuleBuiltinsClinicProviders.SetDebugNodeClinicProviderGen; +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.PythonNativeObject; import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext; 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.NativeCAPISymbol; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions; @@ -56,13 +56,16 @@ import com.oracle.graal.python.lib.PyIterNextNode; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectGetIter; +import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer; import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; +import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.runtime.GilNode; +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; @@ -124,7 +127,7 @@ protected ArgumentClinicProvider getArgumentClinic() { } @Specialization - static long collect(VirtualFrame frame, PythonModule self, @SuppressWarnings("unused") Object level, + static long collect(VirtualFrame frame, PythonModule self, Object level, @Bind Node inliningTarget, @Cached PyObjectGetAttr getAttr, @Cached PyObjectGetIter getIter, @@ -133,7 +136,8 @@ static long collect(VirtualFrame frame, PythonModule self, @SuppressWarnings("un @Cached CallBinaryMethodNode call, @Cached GilNode gil, @Cached GetThreadStateNode getThreadStateNode, - @Cached ExternalFunctionInvokeNode invokeNode, + @Cached CastToJavaIntExactNode castToJavaInt, + @Cached("createFor($node)") BoundaryCallData boundaryCallData, @Cached CheckPrimitiveFunctionResultNode checkPrimitiveFunctionResultNode) { Object callbacks = getAttr.execute(frame, inliningTarget, self, CALLBACKS); Object iter = getIter.execute(frame, inliningTarget, callbacks); @@ -164,10 +168,11 @@ static long collect(VirtualFrame frame, PythonModule self, @SuppressWarnings("un PythonContext pythonContext = PythonContext.get(inliningTarget); // call native 'gc_collect' if C API context is already available if (pythonContext.getCApiContext() != null && pythonContext.getLanguage(inliningTarget).getEngineOption(PythonOptions.PythonGC)) { - Object executable = CApiContext.getNativeSymbol(inliningTarget, SYMBOL); + NativeFunctionPointer executable = CApiContext.getNativeSymbol(inliningTarget, SYMBOL); PythonThreadState threadState = getThreadStateNode.execute(inliningTarget); - Object result = invokeNode.call(frame, inliningTarget, threadState, C_API_TIMING, SYMBOL.getTsName(), executable, level); - res = checkPrimitiveFunctionResultNode.executeLong(threadState, SYMBOL.getTsName(), result); + long lresult = ExternalFunctionInvoker.invokeGCCOLLECT(frame, C_API_TIMING, pythonContext.ensureNativeContext(), boundaryCallData, threadState, executable, + castToJavaInt.execute(inliningTarget, level)); + res = checkPrimitiveFunctionResultNode.executeLong(inliningTarget, threadState, SYMBOL.getTsName(), lresult); } if (phase != null) { phase = STOP; @@ -235,7 +240,7 @@ static PNone disable() { context.getGcState().setEnabled(false); CApiContext cApiContext = context.getCApiContext(); if (cApiContext != null) { - CStructAccess.WriteIntNode.writeUncached(cApiContext.getGCState(), CFields.GCState__enabled, 0); + CStructAccess.writeIntField(cApiContext.getGCState(), CFields.GCState__enabled, 0); } return PNone.NONE; } @@ -251,7 +256,7 @@ static PNone enable() { context.getGcState().setEnabled(true); CApiContext cApiContext = context.getCApiContext(); if (cApiContext != null) { - CStructAccess.WriteIntNode.writeUncached(cApiContext.getGCState(), CFields.GCState__enabled, 1); + CStructAccess.writeIntField(cApiContext.getGCState(), CFields.GCState__enabled, 1); } return PNone.NONE; } @@ -282,7 +287,7 @@ static PNone doGeneric(int flags) { context.getGcState().setDebug(flags); CApiContext cApiContext = context.getCApiContext(); if (cApiContext != null) { - CStructAccess.WriteIntNode.writeUncached(cApiContext.getGCState(), CFields.GCState__debug, flags); + CStructAccess.writeIntField(cApiContext.getGCState(), CFields.GCState__debug, flags); } return PNone.NONE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java index 4429491715..c0725e37d4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java @@ -98,6 +98,7 @@ import com.oracle.graal.python.builtins.modules.GraalPythonModuleBuiltinsFactory.DebugNodeFactory; import com.oracle.graal.python.builtins.modules.cext.PythonCextCapsuleBuiltins; import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.PythonAbstractObject; import com.oracle.graal.python.builtins.objects.array.PArray; import com.oracle.graal.python.builtins.objects.bytes.PBytes; import com.oracle.graal.python.builtins.objects.bytes.PBytesLike; @@ -105,12 +106,12 @@ 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.PySequenceArrayWrapper.ToNativeStorageNode; -import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper; 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.cext.capi.transitions.GetNativeWrapperNode; -import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers; 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.code.CodeNodes; import com.oracle.graal.python.builtins.objects.code.PCode; import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage; @@ -216,6 +217,7 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.strings.TruffleString; +import com.oracle.truffle.api.strings.TruffleString.Encoding; @CoreFunctions(defineModule = J___GRAALPYTHON__, isEager = true) public final class GraalPythonModuleBuiltins extends PythonBuiltins { @@ -303,13 +305,14 @@ public void postInitialize(Python3Core core) { mod.setAttribute(tsLiteral("is_native_object"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("get_handle_table_id"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("is_strong_handle_table_ref"), PNone.NO_VALUE); + mod.setAttribute(tsLiteral("has_id_reference"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("clear_interop_type_registry"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("foreign_number_list"), PNone.NO_VALUE); mod.setAttribute(tsLiteral("foreign_wrapper"), PNone.NO_VALUE); } else { - mod.setAttribute(tsLiteral("using_native_primitive_storage_strategy"), context.getLanguage().getEngineOption(PythonOptions.UseNativePrimitiveStorageStrategy)); mod.setAttribute(tsLiteral("interop_has_gil"), new InteropGilTester()); } + addBuiltinConstant("using_native_primitive_storage_strategy", context.getLanguage().getEngineOption(PythonOptions.UseNativePrimitiveStorageStrategy)); if (PythonImageBuildOptions.WITHOUT_PLATFORM_ACCESS || !context.getOption(PythonOptions.RunViaLauncher)) { mod.setAttribute(tsLiteral("list_files"), PNone.NO_VALUE); } @@ -668,10 +671,9 @@ public abstract static class DebugNode extends PythonBuiltinNode { public abstract Object execute(Object[] args); - @Specialization @TruffleBoundary - public Object doIt(Object[] args) { - PrintWriter stdout = new PrintWriter(getContext().getStandardOut()); + public static Object tdebug(PythonContext context, Object[] args) { + PrintWriter stdout = new PrintWriter(context.getStandardOut()); for (int i = 0; i < args.length; i++) { stdout.println(args[i]); } @@ -679,6 +681,11 @@ public Object doIt(Object[] args) { return PNone.NONE; } + @Specialization + public Object doIt(Object[] args) { + return tdebug(getContext(), args); + } + @NeverDefault public static DebugNode create() { return DebugNodeFactory.create(null); @@ -1231,12 +1238,11 @@ static int doManaged(Object object) { if (object instanceof PythonAbstractNativeObject) { return -1; } - Object nativeWrapper = GetNativeWrapperNode.executeUncached(object); - if (nativeWrapper instanceof PythonNativeWrapper pn) { - return pn.ref.getHandleTableIndex(); - } else { - return -1; + if (object instanceof PythonObject pythonObject) { + long untagged = HandlePointerConverter.pointerToStub(pythonObject.getNativePointer()); + return CStructAccess.readIntField(untagged, CFields.GraalPyObject__handle_table_index); } + return -1; } } @@ -1246,8 +1252,19 @@ abstract static class IsWeakHandleTableRef extends PythonUnaryBuiltinNode { @Specialization @TruffleBoundary static boolean doGeneric(int id) { - PythonObjectReference ref = CApiTransitions.nativeStubLookupGet(PythonContext.get(null).nativeContext, 0, id); - return ref != null && ref.isStrongReference(); + Object ref = CApiTransitions.nativeStubLookupGet(PythonContext.get(null).handleContext, 0, id); + assert ref == null || ref instanceof PythonAbstractObject || ref instanceof PythonObjectReference; + return ref instanceof PythonAbstractObject || ref != null && ((PythonObjectReference) ref).isStrongReference(); + } + } + + @Builtin(name = "has_id_reference", minNumOfPositionalArgs = 1) + @GenerateNodeFactory + abstract static class HasIdReference extends PythonUnaryBuiltinNode { + @Specialization + @TruffleBoundary + static boolean doGeneric(Object object) { + return object instanceof PythonAbstractNativeObject nativeObject && nativeObject.ref != null; } } @@ -1521,16 +1538,20 @@ public abstract static class CreateArrowPyCapsule extends PythonBinaryBuiltinNod @Specialization static PTuple doCreate(long arrowArrayAddr, long arrowSchemaAddr, @Bind Node inliningTarget, - @Cached PythonCextCapsuleBuiltins.PyCapsuleNewNode pyCapsuleNewNode) { - var ctx = getContext(inliningTarget); - + @Cached PythonCextCapsuleBuiltins.PyCapsuleNewNode pyCapsuleNewNode, + @Cached TruffleString.AsNativeNode asNativeNode, + @Cached TruffleString.GetInternalNativePointerNode getInternalNativePointerNode, + @CachedLibrary(limit = "1") InteropLibrary lib) { + PythonContext ctx = getContext(inliningTarget); long arrayDestructor = ctx.arrowSupport.getArrowArrayDestructor(inliningTarget); - var arrayCapsuleName = new CArrayWrappers.CByteArrayWrapper(ArrowArray.CAPSULE_NAME); - PyCapsule arrowArrayCapsule = pyCapsuleNewNode.execute(inliningTarget, arrowArrayAddr, arrayCapsuleName, arrayDestructor); + TruffleString arrayCapsuleName = asNativeNode.execute(ArrowArray.CAPSULE_NAME, ctx::allocateContextMemory, Encoding.UTF_8, false, true); + long arrayCapsuleNamePointer = PythonUtils.coerceToLong(getInternalNativePointerNode.execute(arrayCapsuleName, Encoding.UTF_8), lib); + PyCapsule arrowArrayCapsule = pyCapsuleNewNode.execute(inliningTarget, arrowArrayAddr, arrayCapsuleNamePointer, arrayDestructor); long schemaDestructor = ctx.arrowSupport.getArrowSchemaDestructor(inliningTarget); - var schemaCapsuleName = new CArrayWrappers.CByteArrayWrapper(ArrowSchema.CAPSULE_NAME); - PyCapsule arrowSchemaCapsule = pyCapsuleNewNode.execute(inliningTarget, arrowSchemaAddr, schemaCapsuleName, schemaDestructor); + TruffleString schemaCapsuleName = asNativeNode.execute(ArrowSchema.CAPSULE_NAME, ctx::allocateContextMemory, Encoding.UTF_8, false, true); + long schemaCapsuleNamePointer = PythonUtils.coerceToLong(getInternalNativePointerNode.execute(schemaCapsuleName, Encoding.UTF_8), lib); + PyCapsule arrowSchemaCapsule = pyCapsuleNewNode.execute(inliningTarget, arrowSchemaAddr, schemaCapsuleNamePointer, schemaDestructor); return PFactory.createTuple(ctx.getLanguage(inliningTarget), new Object[]{arrowSchemaCapsule, arrowArrayCapsule}); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java index c1c044aee6..a5b2a2a694 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ImpModuleBuiltins.java @@ -46,6 +46,7 @@ import static com.oracle.graal.python.builtins.modules.ImpModuleBuiltins.FrozenStatus.FROZEN_INVALID; import static com.oracle.graal.python.builtins.modules.ImpModuleBuiltins.FrozenStatus.FROZEN_NOT_FOUND; import static com.oracle.graal.python.builtins.modules.ImpModuleBuiltins.FrozenStatus.FROZEN_OKAY; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___LOADER__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___ORIGNAME__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___PATH__; @@ -125,7 +126,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.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; @@ -260,8 +260,8 @@ static int doPythonModule(VirtualFrame frame, PythonModule extensionModule, @Bind PythonContext context, @Bind Node inliningTarget, @Cached("createFor($node)") BoundaryCallData boundaryCallData) { - Object nativeModuleDef = extensionModule.getNativeModuleDef(); - if (nativeModuleDef == null) { + long nativeModuleDef = extensionModule.getNativeModuleDef(); + if (nativeModuleDef == NULLPTR) { return 0; } PythonLanguage language = context.getLanguage(inliningTarget); @@ -274,13 +274,13 @@ static int doPythonModule(VirtualFrame frame, PythonModule extensionModule, } @TruffleBoundary - private static int doExec(Node node, PythonContext context, PythonModule extensionModule, Object nativeModuleDef) { + private static int doExec(Node node, PythonContext context, PythonModule extensionModule, long nativeModuleDef) { /* * Check if module is already initialized. CPython does that by testing if 'md_state != * NULL'. So, we do the same. */ - Object mdState = extensionModule.getNativeModuleState(); - if (mdState != null && !InteropLibrary.getUncached().isNull(mdState)) { + long mdState = extensionModule.getNativeModuleState(); + if (mdState != NULLPTR) { return 0; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MMapModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MMapModuleBuiltins.java index 14f7c3c6da..8b285a5ec0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MMapModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MMapModuleBuiltins.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,8 +47,12 @@ 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.cext.capi.CExtNodes; +import com.oracle.graal.python.builtins.objects.PythonAbstractObject; +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.mmap.PMMap; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -98,11 +102,17 @@ public MMapModuleBuiltins() { } } + private static final CApiTiming TIMING_MMAP_INIT_BUFFERPROTOCOL = CApiTiming.create(true, NativeCAPISymbol.FUN_MMAP_INIT_BUFFERPROTOCOL); + @Override public void postInitialize(Python3Core core) { super.postInitialize(core); core.getContext().registerCApiHook(() -> { - CExtNodes.PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_MMAP_INIT_BUFFERPROTOCOL, PythonToNativeNode.executeUncached(PythonBuiltinClassType.PMMap)); + PythonAbstractObject promoted = EnsurePythonObjectNode.executeUncached(core.getContext(), PythonBuiltinClassType.PMMap); + ExternalFunctionInvoker.invokeMMAP_INIT_BUFFERPROTOCOL( + TIMING_MMAP_INIT_BUFFERPROTOCOL, + CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_MMAP_INIT_BUFFERPROTOCOL), + PythonToNativeNode.executeLongUncached(promoted)); }); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathGuards.java index 4dfbc620db..ab765cf9d5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathGuards.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MathGuards.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 diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index b576a43423..8adfefcedc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -60,7 +60,6 @@ import static com.oracle.graal.python.builtins.modules.io.IONodes.T_R; import static com.oracle.graal.python.builtins.modules.io.IONodes.T_W; import static com.oracle.graal.python.builtins.modules.io.IONodes.T_WRITE; -import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyObject__ob_refcnt; import static com.oracle.graal.python.builtins.objects.str.StringUtils.cat; import static com.oracle.graal.python.lib.PyTraceBackPrint.castToString; import static com.oracle.graal.python.lib.PyTraceBackPrint.classNameNoDot; @@ -139,6 +138,7 @@ import static com.oracle.graal.python.util.PythonUtils.tsLiteral; import java.io.IOException; +import java.lang.ref.Reference; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.util.Arrays; @@ -168,9 +168,11 @@ import com.oracle.graal.python.builtins.modules.io.TextIOWrapperNodesFactory.TextIOWrapperInitNodeGen; 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.PythonNativeWrapper.PythonAbstractObjectNativeWrapper; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; +import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode; +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.HandlePointerConverter; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem; import com.oracle.graal.python.builtins.objects.dict.PDict; @@ -238,12 +240,14 @@ import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PosixSupportLibrary; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.PythonContext.CApiState; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.formatting.IntegerFormatter; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.CharsetMapping; 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.CompilerDirectives.ValueType; import com.oracle.truffle.api.Truffle; @@ -961,23 +965,34 @@ private static String defaultCharsetName() { public abstract static class GetrefcountNode extends PythonUnaryBuiltinNode { @Specialization - static long doGeneric(PythonAbstractObject object, - @Cached CStructAccess.ReadI64Node read) { - if (object instanceof PythonAbstractNativeObject nativeKlass) { - return read.readFromObj(nativeKlass, PyObject__ob_refcnt); - } - - PythonAbstractObjectNativeWrapper wrapper = object.getNativeWrapper(); - if (wrapper == null) { - return -1; - } else { - return wrapper.getRefCount(); + @TruffleBoundary + static long doGeneric(Object object, + @Bind PythonContext context) { + if (!context.isNativeAccessAllowed()) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new RuntimeException(ErrorMessages.NATIVE_ACCESS_NOT_ALLOWED.toJavaStringUncached()); } - } - - @Fallback - protected long doGeneric(@SuppressWarnings("unused") Object object) { - return -1; + if (context.getCApiState() != CApiState.INITIALIZED) { + if (object instanceof PythonAbstractObject pythonAbstractObject) { + return FirstToNativeNode.getInitialRefcnt(false, PythonToNativeInternalNode.isImmortal(context, pythonAbstractObject)); + } + return PythonObject.MANAGED_REFCNT; + } + /* + * Treat PythonObject separately. We don't want to transform it to native just for + * reading the refcount. If it is not native, then the refcount is MANAGED_REFCNT. + */ + if (object instanceof PythonObject pythonObject) { + return pythonObject.getRefCount(); + } + Object promotedObject = EnsurePythonObjectNode.executeUncached(context, object, false); + long pointer = PythonToNativeInternalNode.executeUncached(promotedObject, false); + if (HandlePointerConverter.pointsToPyIntHandle(pointer) || HandlePointerConverter.pointsToPyFloatHandle(pointer)) { + return PythonObject.IMMORTAL_REFCNT; + } + long refCount = CApiTransitions.readNativeRefCount(HandlePointerConverter.pointsToPyHandleSpace(pointer) ? HandlePointerConverter.pointerToStub(pointer) : pointer); + Reference.reachabilityFence(promotedObject); + return refCount; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextAbstractBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextAbstractBuiltins.java index 24407dbb0f..2c9313427c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextAbstractBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextAbstractBuiltins.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 @@ -43,15 +43,18 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError; 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.modules.cext.PythonCextBuiltins.CApiBuiltinNode.checkNonNullArgUncached; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtr; -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.structs.CFields.PyTypeObject__tp_doc; +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.BuiltinNames.T_SEND; import static com.oracle.graal.python.nodes.ErrorMessages.BASE_MUST_BE; import static com.oracle.graal.python.nodes.ErrorMessages.OBJ_ISNT_MAPPING; @@ -60,6 +63,7 @@ import static com.oracle.graal.python.nodes.SpecialMethodNames.T_KEYS; import static com.oracle.graal.python.nodes.SpecialMethodNames.T_VALUES; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETITEM__; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.BuiltinFunctions.BinNode; @@ -67,28 +71,21 @@ import com.oracle.graal.python.builtins.modules.BuiltinFunctions.OctNode; 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.CApiQuaternaryBuiltinNode; -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.objects.PNone; 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.CExtNodes.AsCharPointerNode; -import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CStringWrapper; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; -import com.oracle.graal.python.builtins.objects.dict.DictBuiltins.ItemsNode; -import com.oracle.graal.python.builtins.objects.dict.DictBuiltins.KeysNode; -import com.oracle.graal.python.builtins.objects.dict.DictBuiltins.ValuesNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode; +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.dict.PDict; import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.getsetdescriptor.GetSetDescriptor; import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes; -import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod; import com.oracle.graal.python.builtins.objects.slice.PSlice; import com.oracle.graal.python.builtins.objects.str.StringBuiltins; -import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins.TupleNode; 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.TypeNodes.IsSameTypeNode; @@ -131,6 +128,7 @@ import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectGetItem; import com.oracle.graal.python.lib.PyObjectLookupAttr; +import com.oracle.graal.python.lib.PyObjectSizeNode; import com.oracle.graal.python.lib.PySequenceCheckNode; import com.oracle.graal.python.lib.PySequenceConcatNode; import com.oracle.graal.python.lib.PySequenceContainsNode; @@ -143,56 +141,50 @@ import com.oracle.graal.python.lib.PySequenceSizeNode; import com.oracle.graal.python.lib.PySliceNew; 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.WriteAttributeToPythonObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; +import com.oracle.graal.python.nodes.builtins.TupleNodes.ConstructTupleNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.exception.PythonErrorType; +import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives; 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.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; public final class PythonCextAbstractBuiltins { - + private static final TruffleLogger PY_OBJECT_SET_DOC_LOGGER = CApiContext.getLogger(PythonCextAbstractBuiltins.class); /////// PyNumber /////// - @CApiBuiltin(name = "_PyNumber_Index", ret = PyObjectTransfer, args = {PyObject}, call = Direct) - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyNumber_Index extends CApiUnaryBuiltinNode { - @Specialization - static Object index(Object obj, - @Bind Node inliningTarget, - @Cached PyNumberIndexNode indexNode, - @Cached PRaiseNode raiseNode) { - checkNonNullArg(inliningTarget, obj, raiseNode); - return indexNode.execute(null, inliningTarget, obj); - } + @CApiBuiltin(name = "_PyNumber_Index", ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct, acquireGil = false) + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct, acquireGil = false) + static long PyNumber_Index(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + checkNonNullArgUncached(obj); + Object result = PyNumberIndexNode.executeUncached(obj); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyNumber_Long extends CApiUnaryBuiltinNode { - - @Specialization - static Object nlong(Object object, - @Bind Node inliningTarget, - @Cached PyNumberLongNode pyNumberLongNode) { - return pyNumberLongNode.execute(null, inliningTarget, object); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Long(long objectPtr) { + Object object = NativeToPythonNode.executeRawUncached(objectPtr); + Object result = PyNumberLongNode.executeUncached(object); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, Int}, call = Direct) + // TODO(CAPI STATIC): uses nodes without @GenerateUncached + @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, Int}, call = Direct, acquireGil = false) protected abstract static class PyNumber_ToBase extends CApiBinaryBuiltinNode { @Specialization(guards = "base == 2") static Object toBase2(Object n, @SuppressWarnings("unused") int base, @@ -241,751 +233,562 @@ protected boolean checkBase(int base) { } } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyNumber_Float extends CApiUnaryBuiltinNode { - - @Specialization - static double doDoubleNativeWrapper(double object) { - return object; - } - - @Specialization - static double doLongNativeWrapper(long object) { - return object; - } - - @Specialization - static Object doGeneric(Object object, - @Bind Node inliningTarget, - @Cached PyNumberFloatNode pyNumberFloat) { - return pyNumberFloat.execute(inliningTarget, object); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Float(long objectPtr) { + Object object = NativeToPythonNode.executeRawUncached(objectPtr); + double result = PyNumberFloatNode.executeUncached(object); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Add extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberAddNode addNode) { - return addNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Add(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberAddNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Subtract extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberSubtractNode subtractNode) { - return subtractNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Subtract(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberSubtractNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Multiply extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberMultiplyNode multiplyNode) { - return multiplyNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Multiply(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberMultiplyNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Remainder extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberRemainderNode remainderNode) { - return remainderNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Remainder(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberRemainderNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_TrueDivide extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberTrueDivideNode trueDivideNode) { - return trueDivideNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_TrueDivide(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberTrueDivideNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_FloorDivide extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberFloorDivideNode floorDivideNode) { - return floorDivideNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_FloorDivide(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberFloorDivideNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Divmod extends CApiBinaryBuiltinNode { - @Specialization - static Object div(Object a, Object b, - @Bind Node inliningTarget, - @Cached PyNumberDivmodNode divmodNode) { - return divmodNode.execute(null, inliningTarget, a, b); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Divmod(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberDivmodNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_And extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberAndNode andNode) { - return andNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_And(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberAndNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Or extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberOrNode orNode) { - return orNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Or(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberOrNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Xor extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberXorNode xorNode) { - return xorNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Xor(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberXorNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Lshift extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberLshiftNode lshiftNode) { - return lshiftNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Lshift(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberLshiftNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Rshift extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberRshiftNode rshiftNode) { - return rshiftNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Rshift(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberRshiftNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_MatrixMultiply extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberMatrixMultiplyNode matrixMultiplyNode) { - return matrixMultiplyNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_MatrixMultiply(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberMatrixMultiplyNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceAdd extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceAddNode addNode) { - return addNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceAdd(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceAddNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceSubtract extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceSubtractNode subtractNode) { - return subtractNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceSubtract(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceSubtractNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceMultiply extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceMultiplyNode multiplyNode) { - return multiplyNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceMultiply(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceMultiplyNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceRemainder extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceRemainderNode remainderNode) { - return remainderNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceRemainder(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceRemainderNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceTrueDivide extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceTrueDivideNode trueDivideNode) { - return trueDivideNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceTrueDivide(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceTrueDivideNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceFloorDivide extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceFloorDivideNode floorDivideNode) { - return floorDivideNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceFloorDivide(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceFloorDivideNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceAnd extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceAndNode andNode) { - return andNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceAnd(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceAndNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceOr extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceOrNode orNode) { - return orNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceOr(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceOrNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceXor extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceXorNode xorNode) { - return xorNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceXor(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceXorNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceLshift extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceLshiftNode lshiftNode) { - return lshiftNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceLshift(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceLshiftNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceRshift extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceRshiftNode rshiftNode) { - return rshiftNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceRshift(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceRshiftNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlaceMatrixMultiply extends CApiBinaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, - @Cached PyNumberInPlaceMatrixMultiplyNode matrixMultiplyNode) { - return matrixMultiplyNode.execute(null, o1, o2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlaceMatrixMultiply(long o1Ptr, long o2Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object result = PyNumberInPlaceMatrixMultiplyNode.getUncached().execute(null, o1, o2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_InPlacePower extends CApiTernaryBuiltinNode { - - @Specialization - static Object doGeneric(Object o1, Object o2, Object o3, - @Cached PyNumberInPlacePowerNode powerNode) { - return powerNode.execute(null, o1, o2, o3); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_InPlacePower(long o1Ptr, long o2Ptr, long o3Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object o3 = NativeToPythonNode.executeRawUncached(o3Ptr); + Object result = PyNumberInPlacePowerNode.getUncached().execute(null, o1, o2, o3); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_PyNumber_Power extends CApiTernaryBuiltinNode { - - @Specialization - Object doGeneric(Object o1, Object o2, Object o3, - @Cached PyNumberPowerNode powerNode) { - return powerNode.execute(null, o1, o2, o3); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static long GraalPyPrivate_PyNumber_Power(long o1Ptr, long o2Ptr, long o3Ptr) { + Object o1 = NativeToPythonNode.executeRawUncached(o1Ptr); + Object o2 = NativeToPythonNode.executeRawUncached(o2Ptr); + Object o3 = NativeToPythonNode.executeRawUncached(o3Ptr); + Object result = PyNumberPowerNode.getUncached().execute(null, o1, o2, o3); + return PythonToNativeNewRefNode.executeLongUncached(result); } /////// PySequence /////// - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PySequence_Tuple extends CApiUnaryBuiltinNode { - - @Specialization - Object values(Object obj, - @Bind Node inliningTarget, - @Cached TupleNode tupleNode, - @Cached GetClassNode getClassNode) { - if (getClassNode.execute(inliningTarget, obj) == PythonBuiltinClassType.PTuple) { - return obj; - } else { - return tupleNode.execute(null, PythonBuiltinClassType.PTuple, obj); - } - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct, acquireGil = false) + static long PySequence_Tuple(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + checkNonNullArgUncached(obj); + Object result = GetClassNode.executeUncached(obj) == PythonBuiltinClassType.PTuple ? obj : ConstructTupleNode.getUncached().execute(null, obj); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PySequence_List extends CApiUnaryBuiltinNode { - @Specialization - Object values(Object obj, - @Cached ConstructListNode listNode) { - return listNode.execute(null, obj); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct, acquireGil = false) + static long PySequence_List(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + Object result = ConstructListNode.getUncached().execute(null, obj); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = Int, args = {PyObject, Py_ssize_t, PyObject}, call = Ignored) - public abstract static class GraalPyPrivate_Sequence_SetItem extends CApiTernaryBuiltinNode { - @Specialization - static Object setItem(Object obj, long key, Object value, - @Bind Node inliningTarget, - @Cached PySequenceSetItemNode setItemNode) { - if ((int) key != key) { - throw PRaiseNode.raiseStatic(inliningTarget, PythonErrorType.OverflowError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, key); - } - setItemNode.execute(null, inliningTarget, obj, (int) key, value); - return 0; + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, Py_ssize_t, PyObjectRawPointer}, call = Ignored, acquireGil = false) + static int GraalPyPrivate_Sequence_SetItem(long objPtr, long key, long valuePtr) { + if ((int) key != key) { + throw PRaiseNode.raiseStatic(null, PythonErrorType.OverflowError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, key); } + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + Object value = NativeToPythonNode.executeRawUncached(valuePtr); + PySequenceSetItemNode.executeUncached(obj, (int) key, value); + return 0; } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, Py_ssize_t, Py_ssize_t}, call = Direct) - abstract static class PySequence_GetSlice extends CApiTernaryBuiltinNode { - - @Specialization(guards = "checkNode.execute(inliningTarget, obj)", limit = "1") - static Object getSlice(Object obj, long iLow, long iHigh, - @Bind Node inliningTarget, - @SuppressWarnings("unused") @Exclusive @Cached PySequenceCheckNode checkNode, - @Cached PyObjectLookupAttr lookupAttrNode, - @Cached PySliceNew sliceNode, - @Cached CallNode callNode) { - Object getItemCallable = lookupAttrNode.execute(null, inliningTarget, obj, T___GETITEM__); - return callNode.executeWithoutFrame(getItemCallable, sliceNode.execute(inliningTarget, iLow, iHigh, PNone.NONE)); - } - - @Specialization(guards = "!checkNode.execute(inliningTarget, obj)", limit = "1") - static Object getSlice(Object obj, @SuppressWarnings("unused") Object key, @SuppressWarnings("unused") Object value, - @SuppressWarnings("unused") @Exclusive @Cached PySequenceCheckNode checkNode, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.OBJ_IS_UNSLICEABLE, obj); + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, Py_ssize_t, Py_ssize_t}, call = Direct, acquireGil = false) + static long PySequence_GetSlice(long objPtr, long iLow, long iHigh) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + if (PySequenceCheckNode.executeUncached(obj)) { + Object getItemCallable = PyObjectLookupAttr.executeUncached(obj, T___GETITEM__); + Object result = CallNode.executeUncached(getItemCallable, PySliceNew.executeUncached(iLow, iHigh, PNone.NONE)); + return PythonToNativeNewRefNode.executeLongUncached(result); } + throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.OBJ_IS_UNSLICEABLE, obj); } - @CApiBuiltin(ret = Int, args = {PyObject, PyObject}, call = Direct) - abstract static class PySequence_Contains extends CApiBinaryBuiltinNode { - - @Specialization - static int contains(Object haystack, Object needle, - @Bind Node inliningTarget, - @Cached PySequenceContainsNode containsNode) { - return PInt.intValue(containsNode.execute(null, inliningTarget, haystack, needle)); - } + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct, acquireGil = false) + static int PySequence_Contains(long haystackPtr, long needlePtr) { + Object haystack = NativeToPythonNode.executeRawUncached(haystackPtr); + Object needle = NativeToPythonNode.executeRawUncached(needlePtr); + return PInt.intValue(PySequenceContainsNode.executeUncached(haystack, needle)); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, Py_ssize_t}, call = Direct) - abstract static class PySequence_InPlaceRepeat extends CApiBinaryBuiltinNode { - @Specialization - static Object repeat(Object obj, long n, - @Bind Node inliningTarget, - @Cached PRaiseNode raiseNode, - @Cached PySequenceInPlaceRepeatNode repeat) { - if (!PInt.isIntRange(n)) { - throw raiseNode.raise(inliningTarget, OverflowError); - } - return repeat.execute(null, inliningTarget, obj, (int) n); + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, Py_ssize_t}, call = Direct) + static long PySequence_InPlaceRepeat(long objPtr, long n) { + if (!PInt.isIntRange(n)) { + throw PRaiseNode.raiseStatic(null, OverflowError); } + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + Object result = PySequenceInPlaceRepeatNode.executeUncached(obj, (int) n); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct) - abstract static class PySequence_Concat extends CApiBinaryBuiltinNode { - @Specialization - Object doIt(Object s1, Object s2, - @Bind Node inliningTarget, - @Cached PySequenceConcatNode pySeqConcat) { - return pySeqConcat.execute(null, inliningTarget, s1, s2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PySequence_Concat(long s1Ptr, long s2Ptr) { + Object s1 = NativeToPythonNode.executeRawUncached(s1Ptr); + Object s2 = NativeToPythonNode.executeRawUncached(s2Ptr); + Object result = PySequenceConcatNode.executeUncached(s1, s2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct) - abstract static class PySequence_InPlaceConcat extends CApiBinaryBuiltinNode { - - @Specialization - static Object concat(Object s1, Object s2, - @Bind Node inliningTarget, - @Cached PySequenceInPlaceConcatNode concat) { - return concat.execute(null, inliningTarget, s1, s2); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PySequence_InPlaceConcat(long s1Ptr, long s2Ptr) { + Object s1 = NativeToPythonNode.executeRawUncached(s1Ptr); + Object s2 = NativeToPythonNode.executeRawUncached(s2Ptr); + Object result = PySequenceInPlaceConcatNode.executeUncached(s1, s2); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = Int, args = {PyObject, Py_ssize_t}, call = Ignored) - abstract static class GraalPyPrivate_Sequence_DelItem extends CApiBinaryBuiltinNode { - @Specialization - static Object run(Object o, long i, - @Bind Node inliningTarget, - @Cached PySequenceDelItemNode delItemNode) { - if ((int) i != i) { - throw PRaiseNode.raiseStatic(inliningTarget, PythonErrorType.OverflowError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, i); - } - delItemNode.execute(null, inliningTarget, o, (int) i); - return 0; + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, Py_ssize_t}, call = Ignored) + static int GraalPyPrivate_Sequence_DelItem(long oPtr, long i) { + if ((int) i != i) { + throw PRaiseNode.raiseStatic(null, PythonErrorType.OverflowError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, i); } + Object o = NativeToPythonNode.executeRawUncached(oPtr); + PySequenceDelItemNode.executeUncached(o, (int) i); + return 0; } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, Py_ssize_t}, call = Ignored) - abstract static class GraalPyPrivate_Sequence_GetItem extends CApiBinaryBuiltinNode { - @Specialization - static Object doManaged(Object delegate, long position, - @Bind Node inliningTarget, - @Cached PySequenceGetItemNode getItemNode) { - if ((int) position != position) { - throw PRaiseNode.raiseStatic(inliningTarget, PythonErrorType.OverflowError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, position); - } - return getItemNode.execute(null, delegate, (int) position); + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, Py_ssize_t}, call = Ignored) + static long GraalPyPrivate_Sequence_GetItem(long delegatePtr, long position) { + if ((int) position != position) { + throw PRaiseNode.raiseStatic(null, PythonErrorType.OverflowError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, position); } + Object delegate = NativeToPythonNode.executeRawUncached(delegatePtr); + Object result = PySequenceGetItemNode.executeUncached(delegate, (int) position); + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Ignored) - abstract static class GraalPyPrivate_Sequence_Size extends CApiUnaryBuiltinNode { - - @Specialization - static int doSequence(Object obj, - @Bind Node inliningTarget, - @Cached PySequenceSizeNode sizeNode) { - return sizeNode.execute(null, inliningTarget, obj); - } + @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer}, call = Ignored) + static long GraalPyPrivate_Sequence_Size(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + return PySequenceSizeNode.executeUncached(obj); } - @CApiBuiltin(ret = Int, args = {PyObject, Py_ssize_t, Py_ssize_t, PyObject}, call = Direct) - abstract static class PySequence_SetSlice extends CApiQuaternaryBuiltinNode { - @Specialization - static int setSlice(Object sequence, Object iLow, Object iHigh, Object s, - @Bind Node inliningTarget, - @Cached GetObjectSlotsNode getSlotsNode, - @Cached CallSlotMpAssSubscriptNode callSetItem, - @Cached PySliceNew sliceNode, - @Cached PRaiseNode raiseNode) { - TpSlots slots = getSlotsNode.execute(inliningTarget, sequence); - if (slots.mp_ass_subscript() != null) { - PSlice slice = sliceNode.execute(inliningTarget, iLow, iHigh, PNone.NONE); - callSetItem.execute(null, inliningTarget, slots.mp_ass_subscript(), sequence, slice, s); - return 0; - } else { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.P_OBJECT_DOESNT_SUPPORT_SLICE_ASSIGNMENT, sequence); - } + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, Py_ssize_t, Py_ssize_t, PyObjectRawPointer}, call = Direct) + static int PySequence_SetSlice(long sequencePtr, long iLow, long iHigh, long sPtr) { + Object sequence = NativeToPythonNode.executeRawUncached(sequencePtr); + TpSlots slots = GetObjectSlotsNode.executeUncached(sequence); + if (slots.mp_ass_subscript() != null) { + Object s = NativeToPythonNode.executeRawUncached(sPtr); + PSlice slice = PySliceNew.executeUncached(iLow, iHigh, PNone.NONE); + CallSlotMpAssSubscriptNode.executeUncached(slots.mp_ass_subscript(), sequence, slice, s); + return 0; + } else { + throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.P_OBJECT_DOESNT_SUPPORT_SLICE_ASSIGNMENT, sequence); } } - @CApiBuiltin(ret = Int, args = {PyObject, Py_ssize_t, Py_ssize_t}, call = Direct) - abstract static class PySequence_DelSlice extends CApiTernaryBuiltinNode { - @Specialization - static int setSlice(Object sequence, Object iLow, Object iHigh, - @Bind Node inliningTarget, - @Cached GetObjectSlotsNode getSlotsNode, - @Cached CallSlotMpAssSubscriptNode callSetItem, - @Cached PySliceNew sliceNode, - @Cached PRaiseNode raiseNode) { - TpSlots slots = getSlotsNode.execute(inliningTarget, sequence); - if (slots.mp_ass_subscript() != null) { - PSlice slice = sliceNode.execute(inliningTarget, iLow, iHigh, PNone.NONE); - callSetItem.execute(null, inliningTarget, slots.mp_ass_subscript(), sequence, slice, PNone.NO_VALUE); - return 0; - } else { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.P_OBJECT_DOESNT_SUPPORT_SLICE_DELETION, sequence); - } + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, Py_ssize_t, Py_ssize_t}, call = Direct) + static int PySequence_DelSlice(long sequencePtr, long iLow, long iHigh) { + Object sequence = NativeToPythonNode.executeRawUncached(sequencePtr); + TpSlots slots = GetObjectSlotsNode.executeUncached(sequence); + if (slots.mp_ass_subscript() != null) { + PSlice slice = PySliceNew.executeUncached(iLow, iHigh, PNone.NONE); + CallSlotMpAssSubscriptNode.executeUncached(slots.mp_ass_subscript(), sequence, slice, PNone.NO_VALUE); + return 0; + } else { + throw PRaiseNode.raiseStatic(null, TypeError, ErrorMessages.P_OBJECT_DOESNT_SUPPORT_SLICE_DELETION, sequence); } } - @CApiBuiltin(ret = Py_ssize_t, args = {PyObject, PyObject}, call = Direct) - abstract static class PySequence_Count extends CApiBinaryBuiltinNode { - - @Specialization - static int contains(Object haystack, Object needle, - @Bind Node inliningTarget, - @Cached PySequenceIterSearchNode searchNode) { - return searchNode.execute(inliningTarget, haystack, needle, PySequenceIterSearchNode.PY_ITERSEARCH_COUNT); - } + @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PySequence_Count(long haystackPtr, long needlePtr) { + Object haystack = NativeToPythonNode.executeRawUncached(haystackPtr); + Object needle = NativeToPythonNode.executeRawUncached(needlePtr); + return PySequenceIterSearchNode.executeUncached(haystack, needle, PySequenceIterSearchNode.PY_ITERSEARCH_COUNT); } - @CApiBuiltin(ret = Py_ssize_t, args = {PyObject, PyObject}, call = Direct) - abstract static class PySequence_Index extends CApiBinaryBuiltinNode { - - @Specialization - static int contains(Object haystack, Object needle, - @Bind Node inliningTarget, - @Cached PySequenceIterSearchNode searchNode) { - return searchNode.execute(inliningTarget, haystack, needle, PySequenceIterSearchNode.PY_ITERSEARCH_INDEX); - } + @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PySequence_Index(long haystackPtr, long needlePtr) { + Object haystack = NativeToPythonNode.executeRawUncached(haystackPtr); + Object needle = NativeToPythonNode.executeRawUncached(needlePtr); + return PySequenceIterSearchNode.executeUncached(haystack, needle, PySequenceIterSearchNode.PY_ITERSEARCH_INDEX); } /////// PyObject /////// - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct) - @CApiBuiltin(name = "GraalPyPrivate_Object_GetItemString", ret = PyObjectTransfer, args = {PyObject, ConstCharPtrAsTruffleString}, call = Ignored) - abstract static class PyObject_GetItem extends CApiBinaryBuiltinNode { - @Specialization - Object doManaged(Object list, Object key, - @Bind Node inliningTarget, - @Cached PyObjectGetItem getItem) { - return getItem.execute(null, inliningTarget, list, key); - } - } - - @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Ignored) - abstract static class GraalPyPrivate_Object_Size extends CApiUnaryBuiltinNode { - - @Specialization - static int doGenericUnboxed(Object obj, - @Bind Node inliningTarget, - @Cached com.oracle.graal.python.lib.PyObjectSizeNode sizeNode) { - // Native objects are handled in C - assert !(obj instanceof PythonAbstractNativeObject); - // TODO: theoretically, it is legal for __LEN__ to return a PythonNativeVoidPtr, - // which is not handled in c.o.g.p.lib.PyObjectSizeNode at this point - return sizeNode.execute(null, inliningTarget, obj); - } - } - - @CApiBuiltin(ret = Py_ssize_t, args = {PyObject, Py_ssize_t}, call = Direct) - abstract static class PyObject_LengthHint extends CApiBinaryBuiltinNode { - - @Specialization - static long doGenericUnboxed(Object obj, long defaultValue, - @Bind Node inliningTarget, - @Cached IteratorNodes.GetLength getLength) { - int len = getLength.execute(null, inliningTarget, obj); - if (len == -1) { - return defaultValue; - } - return len; - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PyObject_GetItem(long objPtr, long keyPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + Object key = NativeToPythonNode.executeRawUncached(keyPtr); + Object result = PyObjectGetItem.executeUncached(obj, key); + return PythonToNativeNewRefNode.executeLongUncached(result); } - /////// PyMapping /////// - - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyMapping_Keys extends CApiUnaryBuiltinNode { - @Specialization(guards = "isBuiltinDict(obj)") - Object keys(PDict obj, - @Cached KeysNode keysNode, - @Shared @Cached ConstructListNode listNode) { - return listNode.execute(null, keysNode.execute(null, obj)); - } - - @Fallback - Object keys(Object obj, - @Bind Node inliningTarget, - @Cached PyObjectGetAttr getAttrNode, - @Cached CallNode callNode, - @Shared @Cached ConstructListNode listNode) { - return getKeys(null, inliningTarget, obj, getAttrNode, callNode, listNode); - } - + @CApiBuiltin(name = "GraalPyPrivate_Object_GetItemString", ret = PyObjectRawPointer, args = {PyObjectRawPointer, ConstCharPtr}, call = Ignored) + static long GraalPyPrivate_Object_GetItemString(long objPtr, long keyPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + Object key = CharPtrToPythonNode.getUncached().execute(keyPtr); + Object result = PyObjectGetItem.executeUncached(obj, key); + return PythonToNativeNewRefNode.executeLongUncached(result); } - private static PList getKeys(VirtualFrame frame, Node inliningTarget, Object obj, PyObjectGetAttr getAttrNode, CallNode callNode, ConstructListNode listNode) { - Object attr = getAttrNode.execute(frame, inliningTarget, obj, T_KEYS); - return listNode.execute(frame, callNode.execute(frame, attr)); + @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer}, call = Ignored) + static long GraalPyPrivate_Object_Size(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + // Native objects are handled in C + assert !(obj instanceof PythonAbstractNativeObject); + // TODO: theoretically, it is legal for __LEN__ to return a PythonNativeVoidPtr, + // which is not handled in c.o.g.p.lib.PyObjectSizeNode at this point + return PyObjectSizeNode.executeUncached(obj); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyMapping_Items extends CApiUnaryBuiltinNode { - @Specialization(guards = "isBuiltinDict(obj)") - static Object items(PDict obj, - @Cached ItemsNode itemsNode, - @Shared @Cached ConstructListNode listNode) { - return listNode.execute(null, itemsNode.execute(null, obj)); - } - - @Fallback - static Object items(Object obj, - @Bind Node inliningTarget, - @Cached PyObjectGetAttr getAttrNode, - @Cached CallNode callNode, - @Shared @Cached ConstructListNode listNode) { - Object attr = getAttrNode.execute(inliningTarget, obj, T_ITEMS); - return listNode.execute(null, callNode.executeWithoutFrame(attr)); + @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer, Py_ssize_t}, call = Direct) + static long PyObject_LengthHint(long objPtr, long defaultValue) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + int len = IteratorNodes.GetLength.executeUncached(obj); + if (len == -1) { + return defaultValue; } + return len; } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyMapping_Values extends CApiUnaryBuiltinNode { - @Specialization(guards = "isBuiltinDict(obj)") - static Object values(PDict obj, - @Shared @Cached ConstructListNode listNode, - @Cached ValuesNode valuesNode) { - return listNode.execute(null, valuesNode.execute(null, obj)); - } + /////// PyMapping /////// - @Fallback - static Object values(Object obj, - @Bind Node inliningTarget, - @Cached PyObjectGetAttr getAttrNode, - @Cached CallNode callNode, - @Shared @Cached ConstructListNode listNode, - @Cached PRaiseNode raiseNode) { - checkNonNullArg(inliningTarget, obj, raiseNode); - Object attr = getAttrNode.execute(inliningTarget, obj, T_VALUES); - return listNode.execute(null, callNode.executeWithoutFrame(attr)); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct) + static long PyMapping_Keys(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + checkNonNullArgUncached(obj); + ConstructListNode listNode = ConstructListNode.getUncached(); + Object listResult; + if (obj instanceof PDict dict && PGuards.isBuiltinDict(dict)) { + PythonLanguage language = PythonContext.get(null).getLanguage(); + Object view = PFactory.createDictKeysView(language, dict); + listResult = listNode.execute(null, view); + } else { + Object callable = PyObjectGetAttr.executeUncached(obj, T_KEYS); + Object view = CallNode.executeUncached(callable); + listResult = listNode.execute(null, view); + } + return PythonToNativeNewRefNode.executeLongUncached(listResult); + } + + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct) + static long PyMapping_Items(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + checkNonNullArgUncached(obj); + ConstructListNode listNode = ConstructListNode.getUncached(); + Object listResult; + if (obj instanceof PDict dict && PGuards.isBuiltinDict(dict)) { + PythonLanguage language = PythonContext.get(null).getLanguage(); + Object view = PFactory.createDictItemsView(language, dict); + listResult = listNode.execute(null, view); + } else { + Object callable = PyObjectGetAttr.executeUncached(obj, T_ITEMS); + Object view = CallNode.executeUncached(callable); + listResult = listNode.execute(null, view); + } + return PythonToNativeNewRefNode.executeLongUncached(listResult); + } + + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct) + static long PyMapping_Values(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + checkNonNullArgUncached(obj); + ConstructListNode listNode = ConstructListNode.getUncached(); + Object listResult; + if (obj instanceof PDict dict && PGuards.isBuiltinDict(dict)) { + PythonLanguage language = PythonContext.get(null).getLanguage(); + Object view = PFactory.createDictValuesView(language, dict); + listResult = listNode.execute(null, view); + } else { + Object callable = PyObjectGetAttr.executeUncached(obj, T_VALUES); + Object view = CallNode.executeUncached(callable); + listResult = listNode.execute(null, view); + } + return PythonToNativeNewRefNode.executeLongUncached(listResult); + } + + @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectRawPointer}, call = Ignored) + static long GraalPyPrivate_Mapping_Size(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + Object cls = GetClassNode.executeUncached(obj); + if (IsSameTypeNode.executeUncached(cls, PythonBuiltinClassType.PSet) || + IsSameTypeNode.executeUncached(cls, PythonBuiltinClassType.PFrozenSet) || + IsSameTypeNode.executeUncached(cls, PythonBuiltinClassType.PDeque)) { + throw PRaiseNode.raiseStatic(null, TypeError, OBJ_ISNT_MAPPING, obj); + } + return PyObjectSizeNode.executeUncached(obj); } - @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Ignored) - abstract static class GraalPyPrivate_Mapping_Size extends CApiUnaryBuiltinNode { + /////// PyIter /////// - // cant use PyMapping_Check: PyMapping_Size returns the __len__ value also for - // subclasses of types not accepted by PyMapping_Check as long they have an overriden - // __len__ method - @Specialization - static int doMapping(Object obj, - @Bind Node inliningTarget, - @Cached com.oracle.graal.python.lib.PyObjectSizeNode sizeNode, - @Cached IsSameTypeNode isSameType, - @Cached GetClassNode getClassNode, - @Cached PRaiseNode raiseNode) { - Object cls = getClassNode.execute(inliningTarget, obj); - if (isSameType.execute(inliningTarget, cls, PythonBuiltinClassType.PSet) || - isSameType.execute(inliningTarget, cls, PythonBuiltinClassType.PFrozenSet) || - isSameType.execute(inliningTarget, cls, PythonBuiltinClassType.PDeque)) { - throw raiseNode.raise(inliningTarget, TypeError, OBJ_ISNT_MAPPING, obj); - } else { - return sizeNode.execute(null, inliningTarget, obj); + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct) + static long PyIter_Next(long iteratorPtr) { + Object iterator = NativeToPythonNode.executeRawUncached(iteratorPtr); + try { + Object result = PyIterNextNode.executeUncached(iterator); + if (result == NATIVE_NULL) { + return NULLPTR; } + return PythonToNativeNewRefNode.executeLongUncached(result); + } catch (IteratorExhausted e) { + return NULLPTR; } } - /////// PyIter /////// - - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyIter_Next extends CApiUnaryBuiltinNode { - @Specialization - Object check(Object object, - @Bind Node inliningTarget, - @Cached PyIterNextNode nextNode) { + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Ignored) + static long GraalPyPrivate_Iter_Send(long iterPtr, long argPtr) { + Object iter = NativeToPythonNode.executeRawUncached(iterPtr); + Object arg = NativeToPythonNode.executeRawUncached(argPtr); + if (arg instanceof PNone && PyIterCheckNode.executeUncached(iter)) { try { - return nextNode.execute(null, inliningTarget, object); + Object result = PyIterNextNode.executeUncached(iter); + if (result == NATIVE_NULL) { + return NULLPTR; + } + return PythonToNativeNewRefNode.executeLongUncached(result); } catch (IteratorExhausted e) { - return getNativeNull(); + return NULLPTR; } } - } - - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Ignored) - abstract static class GraalPyPrivate_Iter_Send extends CApiBinaryBuiltinNode { - @Specialization - Object send(Object iter, Object arg, - @Bind Node inliningTarget, - @Cached PyIterCheckNode pyiterCheck, - @Cached PyObjectCallMethodObjArgs callMethodNode, - @Cached PyIterNextNode nextNode, - @Cached IsBuiltinObjectProfile isClassProfile) { - if (arg instanceof PNone && pyiterCheck.execute(inliningTarget, iter)) { - try { - return nextNode.execute(null, inliningTarget, iter); - } catch (IteratorExhausted e) { - return getNativeNull(); - } - } else { - try { - return callMethodNode.execute(null, inliningTarget, iter, T_SEND, arg); - } catch (PException e) { - e.expectStopIteration(inliningTarget, isClassProfile); - return getNativeNull(); - } + try { + Object result = PyObjectCallMethodObjArgs.executeUncached(iter, T_SEND, arg); + if (result == NATIVE_NULL) { + return NULLPTR; } + return PythonToNativeNewRefNode.executeLongUncached(result); + } catch (PException e) { + e.expectStopIteration(null, IsBuiltinObjectProfile.getUncached()); + return NULLPTR; } } - @CApiBuiltin(ret = ConstCharPtr, args = {PyObject}, call = Direct) - abstract static class PyObject_GetDoc extends CApiUnaryBuiltinNode { - @Specialization - Object get(Object obj, - @Bind Node inliningTarget, - @Cached PyObjectLookupAttr lookupAttr, - @Cached AsCharPointerNode asCharPointerNode) { - try { - Object doc = lookupAttr.execute(null, inliningTarget, obj, T___DOC__); - if (!(doc instanceof PNone)) { - return asCharPointerNode.execute(doc); - } - } catch (PException e) { - // ignore + @CApiBuiltin(ret = ConstCharPtr, args = {PyObjectRawPointer}, call = Direct) + static long PyObject_GetDoc(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + try { + Object doc = PyObjectLookupAttr.executeUncached(obj, T___DOC__); + if (!(doc instanceof PNone)) { + return AsCharPointerNode.getUncached().execute(doc); } - return getNULL(); + } catch (PException e) { + // ignore } + return NULLPTR; } - @CApiBuiltin(ret = Int, args = {PyObject, ConstCharPtrAsTruffleString}, call = Direct) - abstract static class PyObject_SetDoc extends CApiBinaryBuiltinNode { - private static final TruffleLogger LOGGER = CApiContext.getLogger(PyObject_SetDoc.class); - - @Specialization - static int set(PBuiltinFunction obj, Object value, - @Shared("write") @Cached WriteAttributeToPythonObjectNode write) { - write.execute(obj, T___DOC__, value); + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, ConstCharPtr}, call = Direct) + static int PyObject_SetDoc(long objPtr, long valuePtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + Object value = CharPtrToPythonNode.getUncached().execute(valuePtr); + if (obj instanceof PBuiltinFunction builtinFunction) { + WriteAttributeToPythonObjectNode.executeUncached(builtinFunction, T___DOC__, value); return 1; } - - @Specialization - static int set(PBuiltinMethod obj, Object value, - @Shared("write") @Cached WriteAttributeToPythonObjectNode write) { - set(obj.getBuiltinFunction(), value, write); + if (obj instanceof PBuiltinMethod builtinMethod) { + PBuiltinFunction builtinFunction = builtinMethod.getBuiltinFunction(); + WriteAttributeToPythonObjectNode.executeUncached(builtinFunction, T___DOC__, value); return 1; } - - @Specialization - static int set(GetSetDescriptor obj, Object value, - @Shared("write") @Cached WriteAttributeToPythonObjectNode write) { - write.execute(obj, T___DOC__, value); + if (obj instanceof GetSetDescriptor descriptor) { + WriteAttributeToPythonObjectNode.executeUncached(descriptor, T___DOC__, value); return 1; } - - @Specialization(guards = "isType.execute(inliningTarget, type)", limit = "1") - static int set(PythonAbstractNativeObject type, Object value, - @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached IsTypeNode isType, - @Cached TruffleString.SwitchEncodingNode switchEncoding, - @Cached CStructAccess.WritePointerNode writePointerNode) { - Object cValue; - if (value instanceof TruffleString stringValue) { - cValue = new CStringWrapper(switchEncoding.execute(stringValue, TruffleString.Encoding.UTF_8), TruffleString.Encoding.UTF_8); - } else { - cValue = PythonContext.get(inliningTarget).getNativeNull(); + if (obj instanceof PythonAbstractNativeObject nativeType) { + if (IsTypeNode.executeUncached(nativeType)) { + long cValue = NULLPTR; + if (value instanceof TruffleString stringValue) { + cValue = AsCharPointerNode.getUncached().execute(stringValue); + } + writePtrField(nativeType.getPtr(), PyTypeObject__tp_doc, cValue); + return 1; } - writePointerNode.write(type.getPtr(), PyTypeObject__tp_doc, cValue); - return 1; - } - - @Fallback - @SuppressWarnings("unused") - static int set(Object obj, Object value) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - // The callers don't expect errors, so just warn - LOGGER.warning("Unexpected type in PyObject_SetDoc: " + obj.getClass()); - return 1; } + CompilerDirectives.transferToInterpreterAndInvalidate(); + PY_OBJECT_SET_DOC_LOGGER.warning("Unexpected type in PyObject_SetDoc: " + obj.getClass()); + return 1; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextArrayBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextArrayBuiltins.java index d1c0b26fca..27c49c2664 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextArrayBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextArrayBuiltins.java @@ -45,28 +45,28 @@ 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.Int; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_BUFFER_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.capi.transitions.ArgDescriptor.Py_ssize_t; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void; +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.free; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.malloc; -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.objects.PNone; import com.oracle.graal.python.builtins.objects.array.ArrayNodes; import com.oracle.graal.python.builtins.objects.array.PArray; import com.oracle.graal.python.builtins.objects.buffer.BufferFlags; -import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions; +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.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; -import com.oracle.graal.python.runtime.PythonContext; -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.CachedLibrary; -import com.oracle.truffle.api.nodes.Node; +import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.WriteTruffleStringNode; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.strings.TruffleString; public final class PythonCextArrayBuiltins { @@ -74,104 +74,77 @@ public final class PythonCextArrayBuiltins { /** * Graalpy-specific function implemented for Cython */ - @CApiBuiltin(ret = Int, args = {PyObject, Py_ssize_t}, call = Direct) - abstract static class GraalPyArray_Resize extends CApiBinaryBuiltinNode { - @Specialization - static int resize(PArray array, long newSize, - @Bind Node inliningTarget, - @Cached ArrayNodes.EnsureCapacityNode ensureCapacityNode, - @Cached ArrayNodes.SetLengthNode setLengthNode) { - ensureCapacityNode.execute(inliningTarget, array, (int) newSize); - setLengthNode.execute(inliningTarget, array, (int) newSize); - return 0; - } + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, Py_ssize_t}, call = Direct) + static int GraalPyArray_Resize(long arrayPtr, long newSize) { + PArray array = expectArray(arrayPtr, "GraalPyArray_Resize"); + ArrayNodes.EnsureCapacityNode.executeUncached(array, (int) newSize); + ArrayNodes.SetLengthNode.executeUncached(array, (int) newSize); + return 0; } - @CApiBuiltin(ret = CHAR_PTR, args = {PyObject}, call = Direct) - abstract static class GraalPyArray_Data extends CApiUnaryBuiltinNode { - @Specialization - static Object get(PArray array, - @Bind Node inliningTarget, - @Cached ArrayNodes.EnsureNativeStorageNode ensureNativeStorageNode) { - if (array.getBytesLength() > 0) { - return ensureNativeStorageNode.execute(inliningTarget, array).getPtr(); - } else { - return getNativeNull(inliningTarget); - } + @CApiBuiltin(ret = CHAR_PTR, args = {PyObjectRawPointer}, call = Direct) + static long GraalPyArray_Data(long arrayPtr) { + PArray array = expectArray(arrayPtr, "GraalPyArray_Data"); + if (array.getBytesLength() > 0) { + return ArrayNodes.EnsureNativeStorageNode.executeUncached(array).getPtr(); } + return NULLPTR; } - @CApiBuiltin(ret = Int, args = {PyObject, PY_BUFFER_PTR, Int}, call = Ignored) - abstract static class GraalPyPrivate_Array_getbuffer extends CApiTernaryBuiltinNode { - @Specialization - static int getbuffer(PArray array, Object pyBufferPtr, int flags, - @Bind Node inliningTarget, - @Cached ArrayNodes.EnsureNativeStorageNode ensureNativeStorageNode, - @Cached TruffleString.SwitchEncodingNode switchEncodingNode, - @Cached CApiTransitions.PythonToNativeNewRefNode toNativeNewRefNode, - @Cached CStructAccess.WritePointerNode writePointerNode, - @Cached CStructAccess.WriteLongNode writeLongNode, - @Cached CStructAccess.WriteIntNode writeIntNode, - @Cached CStructAccess.WriteTruffleStringNode writeTruffleStringNode, - @Cached CStructAccess.AllocateNode allocateNode) { - Object bufPtr = ensureNativeStorageNode.execute(inliningTarget, array).getPtr(); - Object nativeNull = PythonContext.get(inliningTarget).getNativeNull(); - writePointerNode.write(pyBufferPtr, CFields.Py_buffer__buf, bufPtr); - writePointerNode.write(pyBufferPtr, CFields.Py_buffer__obj, toNativeNewRefNode.execute(array)); - writeLongNode.write(pyBufferPtr, CFields.Py_buffer__len, array.getBytesLength()); - writeIntNode.write(pyBufferPtr, CFields.Py_buffer__readonly, 0); - writeIntNode.write(pyBufferPtr, CFields.Py_buffer__ndim, 1); - writeLongNode.write(pyBufferPtr, CFields.Py_buffer__itemsize, array.getFormat().bytesize); - writePointerNode.write(pyBufferPtr, CFields.Py_buffer__suboffsets, nativeNull); - Object shapePtr = nativeNull; - if ((flags & BufferFlags.PyBUF_ND) == BufferFlags.PyBUF_ND) { - shapePtr = allocateNode.alloc(Long.BYTES); - writeLongNode.write(shapePtr, array.getLength()); - } - writePointerNode.write(pyBufferPtr, CFields.Py_buffer__shape, shapePtr); - Object stridesPtr = nativeNull; - if ((flags & BufferFlags.PyBUF_STRIDES) == BufferFlags.PyBUF_STRIDES) { - stridesPtr = allocateNode.alloc(Long.BYTES); - writeLongNode.write(stridesPtr, array.getFormat().bytesize); - } - writePointerNode.write(pyBufferPtr, CFields.Py_buffer__strides, stridesPtr); - Object formatPtr = nativeNull; - if (((flags & BufferFlags.PyBUF_FORMAT) == BufferFlags.PyBUF_FORMAT)) { - TruffleString format = array.getFormatString(); - // TODO wchar_t check - TruffleString.Encoding formatEncoding = TruffleString.Encoding.US_ASCII; - format = switchEncodingNode.execute(format, formatEncoding); - int formatLen = format.byteLength(formatEncoding); - formatPtr = allocateNode.alloc(formatLen + 1); - writeTruffleStringNode.write(formatPtr, format, formatEncoding); - } - writePointerNode.write(pyBufferPtr, CFields.Py_buffer__format, formatPtr); - writePointerNode.write(pyBufferPtr, CFields.Py_buffer__internal, nativeNull); - - array.getExports().incrementAndGet(); - return 0; + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer, PY_BUFFER_PTR, Int}, call = Ignored) + static int GraalPyPrivate_Array_getbuffer(long arrayPtr, long pyBufferPtr, int flags) { + PArray array = expectArray(arrayPtr, "GraalPyPrivate_Array_getbuffer"); + long bufPtr = ArrayNodes.EnsureNativeStorageNode.executeUncached(array).getPtr(); + writePtrField(pyBufferPtr, CFields.Py_buffer__buf, bufPtr); + writePtrField(pyBufferPtr, CFields.Py_buffer__obj, PythonToNativeNewRefNode.executeLongUncached(array)); + writeLongField(pyBufferPtr, CFields.Py_buffer__len, array.getBytesLength()); + writeIntField(pyBufferPtr, CFields.Py_buffer__readonly, 0); + writeIntField(pyBufferPtr, CFields.Py_buffer__ndim, 1); + writeLongField(pyBufferPtr, CFields.Py_buffer__itemsize, array.getFormat().bytesize); + writePtrField(pyBufferPtr, CFields.Py_buffer__suboffsets, NULLPTR); + long shapePtr = NULLPTR; + if ((flags & BufferFlags.PyBUF_ND) == BufferFlags.PyBUF_ND) { + shapePtr = malloc(Long.BYTES); + NativeMemory.writeLong(shapePtr, array.getLength()); + } + writePtrField(pyBufferPtr, CFields.Py_buffer__shape, shapePtr); + long stridesPtr = NULLPTR; + if ((flags & BufferFlags.PyBUF_STRIDES) == BufferFlags.PyBUF_STRIDES) { + stridesPtr = malloc(Long.BYTES); + NativeMemory.writeLong(stridesPtr, array.getFormat().bytesize); + } + writePtrField(pyBufferPtr, CFields.Py_buffer__strides, stridesPtr); + long formatPtr = NULLPTR; + if ((flags & BufferFlags.PyBUF_FORMAT) == BufferFlags.PyBUF_FORMAT) { + TruffleString format = array.getFormatString(); + // TODO wchar_t check + TruffleString.Encoding formatEncoding = TruffleString.Encoding.US_ASCII; + format = format.switchEncodingUncached(formatEncoding); + int formatLen = format.byteLength(formatEncoding); + formatPtr = calloc(formatLen + 1); + WriteTruffleStringNode.writeUncached(formatPtr, format, formatEncoding); } + writePtrField(pyBufferPtr, CFields.Py_buffer__format, formatPtr); + writePtrField(pyBufferPtr, CFields.Py_buffer__internal, NULLPTR); + + array.getExports().incrementAndGet(); + return 0; } - @CApiBuiltin(ret = Void, args = {PyObject, PY_BUFFER_PTR}, call = Ignored) - abstract static class GraalPyPrivate_Array_releasebuffer extends CApiBinaryBuiltinNode { - @Specialization - static Object releasebuffer(PArray array, Object pyBufferPtr, - @CachedLibrary(limit = "1") InteropLibrary lib, - @Cached CStructAccess.ReadPointerNode readPointerNode, - @Cached CStructAccess.FreeNode freeNode) { - array.getExports().decrementAndGet(); - freeArrayField(pyBufferPtr, CFields.Py_buffer__shape, readPointerNode, lib, freeNode); - freeArrayField(pyBufferPtr, CFields.Py_buffer__strides, readPointerNode, lib, freeNode); - freeArrayField(pyBufferPtr, CFields.Py_buffer__format, readPointerNode, lib, freeNode); - return PNone.NO_VALUE; - } + @CApiBuiltin(ret = Void, args = {PyObjectRawPointer, PY_BUFFER_PTR}, call = Ignored) + static void GraalPyPrivate_Array_releasebuffer(long arrayPtr, long pyBufferPtr) { + PArray array = expectArray(arrayPtr, "GraalPyPrivate_Array_releasebuffer"); + array.getExports().decrementAndGet(); + free(CStructAccess.readPtrField(pyBufferPtr, CFields.Py_buffer__shape)); + free(CStructAccess.readPtrField(pyBufferPtr, CFields.Py_buffer__strides)); + free(CStructAccess.readPtrField(pyBufferPtr, CFields.Py_buffer__format)); + } - private static void freeArrayField(Object pyBufferPtr, CFields cfield, CStructAccess.ReadPointerNode readPointerNode, InteropLibrary lib, CStructAccess.FreeNode freeNode) { - Object field = readPointerNode.read(pyBufferPtr, cfield); - if (!lib.isNull(field)) { - freeNode.free(field); - } + private static PArray expectArray(long arrayPtr, String where) { + Object obj = NativeToPythonNode.executeRawUncached(arrayPtr); + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, !(obj instanceof PArray))) { + throw PythonCextBuiltins.badInternalCall(where, "arrayPtr"); } + return (PArray) obj; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBoolBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBoolBuiltins.java index 622dfea217..19e1def9b8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBoolBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBoolBuiltins.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,27 +41,23 @@ package com.oracle.graal.python.builtins.modules.cext; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored; -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 com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin; -import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiNullaryBuiltinNode; -import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; +import com.oracle.graal.python.runtime.PythonContext; public final class PythonCextBoolBuiltins { - @CApiBuiltin(ret = PyObjectTransfer, call = Ignored) - abstract static class GraalPyPrivate_True extends CApiNullaryBuiltinNode { - @Specialization - static boolean run() { - return true; - } + // These two should only be used during C API initialization, so the performance hardly + // matters, but keeping their codesize small still makes sense. + @CApiBuiltin(ret = PyObjectRawPointer, call = Ignored, acquireGil = false, canRaise = false) + public static long GraalPyPrivate_True() { + return PythonToNativeInternalNode.executeUncached(PythonContext.get(null).getTrue(), true); } - @CApiBuiltin(ret = PyObjectTransfer, call = Ignored) - abstract static class GraalPyPrivate_False extends CApiNullaryBuiltinNode { - @Specialization - static boolean run() { - return false; - } + @CApiBuiltin(ret = PyObjectRawPointer, call = Ignored, acquireGil = false, canRaise = false) + public static long GraalPyPrivate_False() { + return PythonToNativeInternalNode.executeUncached(PythonContext.get(null).getFalse(), true); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java index 631acc4906..23cc849dab 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java @@ -49,16 +49,15 @@ import static com.oracle.graal.python.builtins.objects.cext.capi.CApiContext.GC_LOGGER; import static com.oracle.graal.python.builtins.objects.cext.capi.CApiGCSupport.NEXT_MASK_UNREACHABLE; 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.ConstCharPtrAsTruffleString; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtr; 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.PyCodeObject; -import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyFrameObjectTransfer; -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.PyCodeObjectRawPointer; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyFrameObjectRawPointer; +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.PyTypeObject; -import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObjectTransfer; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObjectRawPointer; 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; @@ -76,12 +75,21 @@ import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMemberDef__name; import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMemberDef__offset; import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyMemberDef__type; +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.builtins.objects.cext.structs.CStructAccess.readStructArrayIntField; +import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayLongField; +import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readStructArrayPtrField; +import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeLongField; import static com.oracle.graal.python.nodes.BuiltinNames.T_BUILTINS; import static com.oracle.graal.python.nodes.BuiltinNames.T__WEAKREF; import static com.oracle.graal.python.nodes.ErrorMessages.INDEX_OUT_OF_RANGE; import static com.oracle.graal.python.nodes.ErrorMessages.NATIVE_S_SUBTYPES_NOT_IMPLEMENTED; import static com.oracle.graal.python.nodes.HiddenAttr.NATIVE_SLOTS; -import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readLongArrayElement; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readPtrArrayElement; import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; @@ -94,23 +102,20 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; -import org.graalvm.collections.Pair; - 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.modules.GraalPythonModuleBuiltins.DebugNode; import com.oracle.graal.python.builtins.modules.SysModuleBuiltins.GetFileSystemEncodingNode; -import com.oracle.graal.python.builtins.modules.cext.PythonCextTypeBuiltins.GraalPyPrivate_Type_AddGetSet; -import com.oracle.graal.python.builtins.modules.cext.PythonCextTypeBuiltins.GraalPyPrivate_Type_AddMember; 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; @@ -119,23 +124,23 @@ import com.oracle.graal.python.builtins.objects.cext.capi.CApiFunction; import com.oracle.graal.python.builtins.objects.cext.capi.CApiGCSupport.PyObjectGCDelNode; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode; -import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.FromCharPointerNodeGen; -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.PThreadState; 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.CharPtrToPythonNode; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.GcNativePtrToPythonNode; 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.NativePtrToPythonWrapperNode; -import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.UpdateStrongRefNode; -import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CoerceNativePointerToLongNode; +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.NativeToPythonNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.UpdateHandleTableReferenceNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ToNativeTypeNode; import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeCachedNode; 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.NativePointer; import com.oracle.graal.python.builtins.objects.cext.structs.CConstants; import com.oracle.graal.python.builtins.objects.cext.structs.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; @@ -143,7 +148,6 @@ import com.oracle.graal.python.builtins.objects.code.PCode; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.exception.PBaseException; -import com.oracle.graal.python.builtins.objects.frame.PFrame; import com.oracle.graal.python.builtins.objects.frame.PFrame.Reference; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.ints.PInt; @@ -163,7 +167,6 @@ 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.TypeNodes; -import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetMroStorageNode; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.HiddenAttr; @@ -187,6 +190,8 @@ import com.oracle.graal.python.runtime.exception.ExceptionUtils; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.exception.PythonErrorType; +import com.oracle.graal.python.runtime.nativeaccess.NativeSignature; +import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.NativeByteSequenceStorage; @@ -194,9 +199,11 @@ import com.oracle.graal.python.util.BufferFormat; 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.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.dsl.Bind; @@ -206,7 +213,6 @@ 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.NonIdempotent; import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.Specialization; @@ -214,20 +220,11 @@ import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.frame.FrameInstanceVisitor; import com.oracle.truffle.api.frame.VirtualFrame; -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; +import com.oracle.truffle.api.nodes.EncapsulatingNodeReference; import com.oracle.truffle.api.nodes.ExplodeLoop; -import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; -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 final class PythonCextBuiltins { @@ -270,6 +267,12 @@ static PythonBuiltinObject doDouble(double d, return PFactory.createFloat(language, d); } + @Specialization + static PythonBuiltinObject doBoolean(boolean b, + @Bind PythonContext context) { + return b ? context.getTrue() : context.getFalse(); + } + static boolean isNaN(double d) { return Double.isNaN(d); } @@ -322,28 +325,15 @@ public static PException checkThrowableBeforeNative(Throwable t, String where1, throw PRaiseNode.raiseStatic(null, SystemError, ErrorMessages.INTERNAL_EXCEPTION_OCCURED); } + static PException badInternalCall(String where, String argName) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw PRaiseNode.raiseStatic(null, SystemError, ErrorMessages.S_S_BAD_ARG_TO_INTERNAL_FUNC, where, argName); + } + public abstract static class CApiBuiltinNode extends PNodeWithContext { public abstract Object execute(Object[] args); - protected final NativePointer getNativeNull() { - return getContext().getNativeNull(); - } - - protected static NativePointer getNativeNull(Node inliningTarget) { - return PythonContext.get(inliningTarget).getNativeNull(); - } - - /** - * Returns the "NULL" pointer retrieved from the native backend, e.g., an LLVMPointer - * instance. This is not wrapped, i.e., it cannot be passed through a PyObject - * Python-To-Native transition (because it would be treated as a foreign Truffle object at - * that point). - */ - protected final Object getNULL() { - return getContext().getNativeNull(); - } - @TruffleBoundary(allowInlining = true) protected static ByteBuffer wrap(byte[] data) { return ByteBuffer.wrap(data); @@ -368,9 +358,13 @@ protected final CApiContext getCApiContext() { return getContext().getCApiContext(); } + protected static CApiContext getStaticCApiContext() { + return PythonContext.get(null).getCApiContext(); + } + protected final PException badInternalCall(String argName) { CompilerDirectives.transferToInterpreterAndInvalidate(); - throw PRaiseNode.raiseStatic(this, SystemError, ErrorMessages.S_S_BAD_ARG_TO_INTERNAL_FUNC, getName(), argName); + throw PythonCextBuiltins.badInternalCall(getName(), argName); } @NonIdempotent @@ -424,6 +418,12 @@ protected final int castToInt(long elementSize) { throw PRaiseNode.raiseStatic(this, SystemError, INDEX_OUT_OF_RANGE); } + protected static void checkNonNullArgUncached(Object obj) { + if (obj == PNone.NO_VALUE) { + throw PRaiseNode.raiseStatic(null, SystemError, ErrorMessages.NULL_ARG_INTERNAL); + } + } + protected static void checkNonNullArg(Node inliningTarget, Object obj, PRaiseNode raiseNode) { if (obj == PNone.NO_VALUE) { throw raiseNode.raise(inliningTarget, SystemError, ErrorMessages.NULL_ARG_INTERNAL); @@ -585,43 +585,33 @@ public final Object execute(Object[] args) { } } - @ExportLibrary(InteropLibrary.class) - public static final class CApiBuiltinExecutable implements TruffleObject { + public static final class CApiBuiltinExecutable { private final CApiTiming timing; private final ArgDescriptor ret; private final ArgDescriptor[] args; private final boolean acquireGil; - @CompilationFinal private CallTarget callTarget; - private final CApiCallPath call; private final String name; private final int id; - public CApiBuiltinExecutable(String name, CApiCallPath call, ArgDescriptor ret, ArgDescriptor[] args, boolean acquireGil, int id) { + public CApiBuiltinExecutable(String name, ArgDescriptor ret, ArgDescriptor[] args, boolean acquireGil, int id) { this.timing = CApiTiming.create(false, name); this.name = name; - this.call = call; this.ret = ret; this.args = args; this.acquireGil = acquireGil; this.id = id; } - CallTarget getCallTarget() { - if (callTarget == null) { - throw CompilerDirectives.shouldNotReachHere("call target slow path not implemented"); + public RootCallTarget getCallTarget() { + PythonLanguage lang = PythonLanguage.get(null); + RootCallTarget ct = lang.getCapiCallTarget(id); + if (ct == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + ct = new ExecuteCApiBuiltinRootNode(this).getCallTarget(); + lang.setCapiCallTarget(id, ct); } - return callTarget; - } - - @SuppressWarnings("static-method") - @ExportMessage - public boolean isExecutable() { - return true; - } - - public CApiCallPath call() { - return call; + return ct; } public String name() { @@ -636,8 +626,17 @@ CExtToNativeNode createRetNode() { return ret.createPythonToNativeNode(); } - ArgDescriptor getRetDescriptor() { - return ret; + Object getErrorReturnValue() { + return switch (ret.getNativeSimpleType()) { + case VOID -> PNone.NO_VALUE; + case SINT8 -> (byte) -1; + case SINT16 -> (short) -1; + case SINT32 -> -1; + case SINT64 -> -1L; + case FLOAT -> -1.0f; + case DOUBLE -> -1.0; + case RAW_POINTER -> NULLPTR; + }; } CExtToJavaNode[] createArgNodes() { @@ -650,113 +649,26 @@ public CApiBuiltinNode createBuiltinNode() { return node; } - public CApiBuiltinNode getUncachedNode() { - // TODO: how to set "node.ret"? - throw CompilerDirectives.shouldNotReachHere("not supported - uncached for " + name); - } - - @ExportMessage - static final class Execute { - @Specialization(guards = "self == cachedSelf", limit = "3") - public static Object doExecute(@SuppressWarnings("unused") CApiBuiltinExecutable self, Object[] arguments, - @Cached("self") CApiBuiltinExecutable cachedSelf, - @Cached(parameters = "cachedSelf") ExecuteCApiBuiltinNode call) { - - try { - return call.execute(cachedSelf, arguments); - } catch (ThreadDeath t) { - CompilerDirectives.transferToInterpreter(); - throw t; - } catch (Throwable t) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - t.printStackTrace(); - throw CompilerDirectives.shouldNotReachHere(t); - } - } - - @Specialization - public static Object doFallback(@SuppressWarnings("unused") CApiBuiltinExecutable self, @SuppressWarnings("unused") Object[] arguments) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw CompilerDirectives.shouldNotReachHere("shouldn't hit generic case of " + Execute.class.getName()); - } - } - - @ExportMessage @TruffleBoundary - boolean isPointer() { - long pointer = PythonContext.get(null).getCApiContext().getClosurePointer(this); - return pointer != -1; - } - - @ExportMessage - @TruffleBoundary - long asPointer() throws UnsupportedMessageException { - long pointer = PythonContext.get(null).getCApiContext().getClosurePointer(this); - if (pointer == -1) { - throw UnsupportedMessageException.create(); - } - return pointer; - } - - private static final class SignatureContainerRootNode extends RootNode { - - final HashMap libs = new HashMap<>(); - - protected SignatureContainerRootNode() { - super(null); - } - - @Override - public Object execute(VirtualFrame frame) { - throw CompilerDirectives.shouldNotReachHere("not meant to be executed"); - } - - public SignatureLibrary getLibrary(String name) { - return libs.computeIfAbsent(name, n -> { - SignatureLibrary lib = SignatureLibrary.getFactory().createDispatched(3); - SignatureContainerRootNode.this.insert(lib); - return lib; - }); - } - } - - @ExportMessage - @TruffleBoundary - void toNative() { + public long getNativePointer() { PythonContext context = PythonContext.get(null); long pointer = context.getCApiContext().getClosurePointer(this); if (pointer == -1) { - if (context.signatureContainer == null) { - context.signatureContainer = new SignatureContainerRootNode().getCallTarget(); + NativeSimpleType[] argTypes = new NativeSimpleType[args.length]; + for (int i = 0; i < args.length; i++) { + argTypes[i] = args[i].getNativeSimpleType(); } - + NativeSignature signature = NativeSignature.create(ret.getNativeSimpleType(), argTypes); try { - SignatureContainerRootNode container = (SignatureContainerRootNode) context.signatureContainer.getRootNode(); - // create NFI closure and get its address - boolean panama = PythonOptions.UsePanama.getValue(context.getEnv().getOptions()); - StringBuilder signature = new StringBuilder(panama ? "with panama (" : "("); - for (int i = 0; i < args.length; i++) { - signature.append(i == 0 ? "" : ","); - signature.append(args[i].getNFISignature()); - } - signature.append("):").append(ret.getNFISignature()); - - Object nfiSignature = context.getEnv().parseInternal(Source.newBuilder(J_NFI_LANGUAGE, signature.toString(), "exec").build()).call(); - Object closure = container.getLibrary(name).createClosure(nfiSignature, this); - InteropLibrary lib = InteropLibrary.getUncached(closure); - lib.toNative(closure); - try { - pointer = lib.asPointer(closure); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - context.getCApiContext().setClosurePointer(closure, null, this, pointer); + pointer = signature.createClosure(context.ensureNativeContext(), name, PythonCextBuiltinRegistry.getMethodHandle(id)); + context.getCApiContext().setClosurePointer(null, this, pointer); LOGGER.finer(CApiBuiltinExecutable.class.getSimpleName() + " toNative: " + id + " / " + name() + " -> " + pointer); } catch (Throwable t) { t.printStackTrace(new PrintStream(context.getEnv().err())); throw t; } } + return pointer; } @Override @@ -765,26 +677,51 @@ public String toString() { } } - @GenerateUncached - abstract static class ExecuteCApiBuiltinNode extends Node { - abstract Object execute(CApiBuiltinExecutable self, Object[] arguments); + static final MethodHandle handle_executeBuiltinWrapper; - public static ExecuteCApiBuiltinNode create(CApiBuiltinExecutable self) { - try { - return new CachedExecuteCApiBuiltinNode(self); - } catch (Throwable t) { - PNodeWithContext.printStack(); - LOGGER.logp(Level.SEVERE, "ExecuteCApiBuiltinNode", "create", "while creating CApiBuiltin " + self.name, t); - throw t; - } + static { + MethodType callType = MethodType.methodType(Object.class, CallTarget.class, Object[].class); + try { + handle_executeBuiltinWrapper = MethodHandles.lookup().findStatic(PythonCextBuiltins.class, "executeBuiltinWrapper", callType); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw CompilerDirectives.shouldNotReachHere(ex); + } + } + + // TODO(native-access) generate a static method for each builtin with concrete arg conversions, + // get rid + // of this wrapper and RootNode + static Object executeBuiltinWrapper(CallTarget callTarget, Object[] args) { + return callTarget.call(args); + } + + static final class ExecuteCApiBuiltinRootNode extends RootNode { + + final CApiBuiltinExecutable self; + @Child ExecuteCApiBuiltinNode executeBuiltinNode; + + ExecuteCApiBuiltinRootNode(CApiBuiltinExecutable self) { + super(PythonLanguage.get(null)); + this.self = self; } - public static ExecuteCApiBuiltinNode getUncached(@SuppressWarnings("unused") CApiBuiltinExecutable self) { - return UncachedExecuteCApiBuiltinNode.INSTANCE; + @Override + public Object execute(VirtualFrame frame) { + if (executeBuiltinNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + executeBuiltinNode = insert(ExecuteCApiBuiltinNode.create(self)); + } + try { + Object[] args = frame.getArguments(); + return executeBuiltinNode.execute(args); + } catch (Throwable e) { + throw CompilerDirectives.shouldNotReachHere(e); + } } } - static final class CachedExecuteCApiBuiltinNode extends ExecuteCApiBuiltinNode { + static final class ExecuteCApiBuiltinNode extends Node { + private final CApiBuiltinExecutable cachedSelf; @Child private GilNode gilNode = GilNode.create(); @Child private CExtToNativeNode retNode; @@ -792,16 +729,25 @@ static final class CachedExecuteCApiBuiltinNode extends ExecuteCApiBuiltinNode { @Child private CApiBuiltinNode builtinNode; @Child private TransformPExceptionToNativeCachedNode transformExceptionToNativeNode; - CachedExecuteCApiBuiltinNode(CApiBuiltinExecutable cachedSelf) { - assert cachedSelf.ret.createCheckResultNode() == null : "primitive result check types are only intended for ExternalFunctionInvokeNode"; + public static ExecuteCApiBuiltinNode create(CApiBuiltinExecutable self) { + try { + return new ExecuteCApiBuiltinNode(self); + } catch (Throwable t) { + PNodeWithContext.printStack(); + LOGGER.logp(Level.SEVERE, "ExecuteCApiBuiltinNode", "create", "while creating CApiBuiltin " + self.name, t); + throw t; + } + } + + ExecuteCApiBuiltinNode(CApiBuiltinExecutable cachedSelf) { this.cachedSelf = cachedSelf; this.retNode = cachedSelf.createRetNode(); this.argNodes = cachedSelf.createArgNodes(); this.builtinNode = cachedSelf.createBuiltinNode(); } - @Override - Object execute(CApiBuiltinExecutable self, Object[] arguments) { + public Object execute(Object[] arguments) { + CApiBuiltinExecutable self = cachedSelf; boolean wasAcquired = self.acquireGil() && gilNode.acquire(); CApiTiming.enter(); try { @@ -829,18 +775,7 @@ Object execute(CApiBuiltinExecutable self, Object[] arguments) { transformExceptionToNativeNode = insert(TransformPExceptionToNativeCachedNode.create()); } transformExceptionToNativeNode.execute(e); - if (cachedSelf.getRetDescriptor().isIntType()) { - return -1; - } else if (cachedSelf.getRetDescriptor().isPyObjectOrPointer()) { - return PythonContext.get(this).getNativeNull(); - } else if (cachedSelf.getRetDescriptor().isFloatType()) { - return -1.0; - } else if (cachedSelf.getRetDescriptor().isVoid()) { - return PNone.NO_VALUE; - } else { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw CompilerDirectives.shouldNotReachHere("return type while handling PException: " + cachedSelf.getRetDescriptor() + " in " + self.name); - } + return cachedSelf.getErrorReturnValue(); } finally { gilNode.release(wasAcquired); CApiTiming.exit(self.timing); @@ -850,26 +785,12 @@ Object execute(CApiBuiltinExecutable self, Object[] arguments) { @ExplodeLoop private void castArguments(Object[] arguments, Object[] argCast) { for (int i = 0; i < argNodes.length; i++) { - argCast[i] = argNodes[i] == null ? arguments[i] : argNodes[i].execute(arguments[i]); + Object arg = arguments[i]; + argCast[i] = argNodes[i] == null ? arg : argNodes[i].execute(arg); } } } - static final class UncachedExecuteCApiBuiltinNode extends ExecuteCApiBuiltinNode { - - static final UncachedExecuteCApiBuiltinNode INSTANCE = new UncachedExecuteCApiBuiltinNode(); - - @Override - public boolean isAdoptable() { - return false; - } - - @Override - Object execute(CApiBuiltinExecutable self, Object[] arguments) { - return IndirectCallNode.getUncached().call(self.getCallTarget(), arguments); - } - } - /** * How the call is routed from native code to the Java CApiBuiltin implementation, i.e., whether * there needs to be some intermediate C code. @@ -912,7 +833,7 @@ public enum CApiCallPath { Ignored, } - @Target(ElementType.TYPE) + @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface CApiBuiltins { CApiBuiltin[] value(); @@ -928,7 +849,7 @@ public enum CApiCallPath { * also used in {@link CApiFunction} to list all functions that are implemented in C code or * that are not currently implemented. */ - @Target(ElementType.TYPE) + @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) @Repeatable(value = CApiBuiltins.class) public @interface CApiBuiltin { @@ -957,6 +878,13 @@ public enum CApiCallPath { */ boolean acquireGil() default true; + /** + * Whether this builtin may raise any kind of exception. Most do, but some can never raise, + * in which case we can omit a lot of code in the catch block and thus maybe produce better + * code. + */ + boolean canRaise() default true; + /** * @see CApiCallPath */ @@ -968,41 +896,32 @@ public enum CApiCallPath { String comment() default ""; } - @CApiBuiltin(ret = PyObjectTransfer, call = Ignored) - abstract static class GraalPyPrivate_FileSystemDefaultEncoding extends CApiNullaryBuiltinNode { - @Specialization - static TruffleString encoding() { - return GetFileSystemEncodingNode.getFileSystemEncoding(); - } + @CApiBuiltin(ret = PyObjectRawPointer, call = Ignored) + static long GraalPyPrivate_FileSystemDefaultEncoding() { + return PythonToNativeNewRefNode.executeLongUncached(GetFileSystemEncodingNode.getFileSystemEncoding()); } - @CApiBuiltin(ret = PyTypeObjectTransfer, args = {ConstCharPtrAsTruffleString}, call = Ignored) - abstract static class GraalPyPrivate_Type extends CApiUnaryBuiltinNode { - - private static final TruffleString[] LOOKUP_MODULES = new TruffleString[]{ - T__WEAKREF, - T_BUILTINS - }; - - @Specialization - static Object doI(TruffleString typeName, - @Bind Node inliningTarget, - @Cached TruffleString.EqualNode eqNode, - @Cached PRaiseNode raiseNode) { - Python3Core core = PythonContext.get(inliningTarget); - for (PythonBuiltinClassType type : PythonBuiltinClassType.VALUES) { - if (eqNode.execute(type.getName(), typeName, TS_ENCODING)) { - return core.lookupType(type); - } + private static final TruffleString[] TYPE_LOOKUP_MODULES = new TruffleString[]{ + T__WEAKREF, + T_BUILTINS + }; + + @CApiBuiltin(ret = PyTypeObjectRawPointer, args = {ConstCharPtr}, call = Ignored) + static long GraalPyPrivate_Type(long typeNamePtr) { + TruffleString typeName = (TruffleString) CharPtrToPythonNode.getUncached().execute(typeNamePtr); + Python3Core core = PythonContext.get(null).getCore(); + for (PythonBuiltinClassType type : PythonBuiltinClassType.VALUES) { + if (type.getName().equalsUncached(typeName, TS_ENCODING)) { + return PythonToNativeNewRefNode.executeLongUncached(core.lookupType(type)); } - for (TruffleString module : LOOKUP_MODULES) { - Object attribute = core.lookupBuiltinModule(module).getAttribute(typeName); - if (attribute != PNone.NO_VALUE) { - return attribute; - } + } + for (TruffleString module : TYPE_LOOKUP_MODULES) { + Object attribute = core.lookupBuiltinModule(module).getAttribute(typeName); + if (attribute != PNone.NO_VALUE) { + return PythonToNativeNewRefNode.executeLongUncached(attribute); } - throw raiseNode.raise(inliningTarget, PythonErrorType.KeyError, ErrorMessages.APOSTROPHE_S, typeName); } + throw PRaiseNode.raiseStatic(null, PythonErrorType.KeyError, ErrorMessages.APOSTROPHE_S, typeName); } @GenerateInline @@ -1030,20 +949,16 @@ static void doObject(PythonObject object, TruffleString key, Object value, } } - @CApiBuiltin(ret = Int, args = {PyTypeObject, Pointer, Pointer}, call = Ignored) - abstract static class GraalPyPrivate_Set_Native_Slots extends CApiTernaryBuiltinNode { - - @Specialization - static int doPythonClass(PythonManagedClass pythonClass, Object nativeGetSets, Object nativeMembers, - @Bind Node inliningTarget, - @Cached HiddenAttr.WriteNode writeAttrNode) { - writeAttrNode.execute(inliningTarget, pythonClass, NATIVE_SLOTS, new Object[]{nativeGetSets, nativeMembers}); - return 0; - } + @CApiBuiltin(ret = Int, args = {PyTypeObjectRawPointer, Pointer, Pointer}, call = Ignored) + static int GraalPyPrivate_Set_Native_Slots(long pythonClassPtr, long nativeGetSets, long nativeMembers) { + PythonManagedClass pythonClass = (PythonManagedClass) NativeToPythonClassInternalNode.executeUncached(pythonClassPtr); + HiddenAttr.WriteNode.executeUncached(pythonClass, NATIVE_SLOTS, new long[]{nativeGetSets, nativeMembers}); + return 0; } - @CApiBuiltin(ret = Void, args = {PyTypeObject}, call = Ignored) - abstract static class GraalPyPrivate_AddInheritedSlots extends CApiUnaryBuiltinNode { + @CApiBuiltin(ret = Void, args = {PyTypeObjectRawPointer}, call = Ignored) + @TruffleBoundary + static void GraalPyPrivate_AddInheritedSlots(long pythonClassPtr) { /** * A native class may inherit from a managed class. However, the managed class may define * custom slots at a time where the C API is not yet loaded. So we need to check if any of @@ -1054,152 +969,138 @@ abstract static class GraalPyPrivate_AddInheritedSlots extends CApiUnaryBuiltinN * we transfer the result of that inheritance process to the slots mirror on the managed * side. */ - @TruffleBoundary - @Specialization - static Object addInheritedSlots(PythonAbstractNativeObject pythonClass, - @Bind Node inliningTarget, - @CachedLibrary(limit = "3") InteropLibrary lib, - @Cached CStructAccess.ReadObjectNode readNativeDict, - @Cached CStructAccess.ReadPointerNode readPointer, - @Cached CStructAccess.ReadI32Node readI32, - @Cached CStructAccess.ReadI64Node readI64, - @Cached FromCharPointerNode fromCharPointer, - @Cached GraalPyPrivate_Type_AddGetSet addGetSet, - @Cached GraalPyPrivate_Type_AddMember addMember, - @Cached GetMroStorageNode getMroStorageNode) { - pythonClass.setTpSlots(TpSlots.fromNative(pythonClass, getCApiContext(inliningTarget).getContext())); - - Object[] getsets = collect(getMroStorageNode.execute(inliningTarget, pythonClass), INDEX_GETSETS); - Object[] members = collect(getMroStorageNode.execute(inliningTarget, pythonClass), INDEX_MEMBERS); - - PDict dict = (PDict) readNativeDict.readFromObj(pythonClass, CFields.PyTypeObject__tp_dict); - - for (Object getset : getsets) { - if (!PGuards.isNullOrZero(getset, lib)) { - for (int i = 0;; i++) { - Object namePtr = readPointer.readStructArrayElement(getset, i, PyGetSetDef__name); - if (PGuards.isNullOrZero(namePtr, lib)) { - break; - } - TruffleString name = fromCharPointer.execute(namePtr); - Object getter = readPointer.readStructArrayElement(getset, i, PyGetSetDef__get); - Object setter = readPointer.readStructArrayElement(getset, i, PyGetSetDef__set); - Object docPtr = readPointer.readStructArrayElement(getset, i, PyGetSetDef__doc); - Object doc = PGuards.isNullOrZero(docPtr, lib) ? PNone.NO_VALUE : fromCharPointer.execute(docPtr); - Object closure = readPointer.readStructArrayElement(getset, i, PyGetSetDef__closure); - - addGetSet.execute(pythonClass, dict, name, getter, setter, doc, closure); + PythonAbstractNativeObject pythonClass = (PythonAbstractNativeObject) NativeToPythonClassInternalNode.executeUncached(pythonClassPtr); + pythonClass.setTpSlots(TpSlots.fromNative(pythonClass, PythonContext.get(null))); + + long[] getsets = collect(TypeNodes.GetMroStorageNode.executeUncached(pythonClass), INDEX_GETSETS); + long[] members = collect(TypeNodes.GetMroStorageNode.executeUncached(pythonClass), INDEX_MEMBERS); + + PDict dict = (PDict) CStructAccess.ReadObjectNode.getUncached().readFromObj(pythonClass, CFields.PyTypeObject__tp_dict); + + for (long getset : getsets) { + if (getset != NULLPTR) { + for (int i = 0;; i++) { + long namePtr = readStructArrayPtrField(getset, i, PyGetSetDef__name); + if (namePtr == NULLPTR) { + break; } + long getter = readStructArrayPtrField(getset, i, PyGetSetDef__get); + long setter = readStructArrayPtrField(getset, i, PyGetSetDef__set); + long docPtr = readStructArrayPtrField(getset, i, PyGetSetDef__doc); + Object doc = docPtr == NULLPTR ? PNone.NO_VALUE : FromCharPointerNode.executeUncached(docPtr); + long closure = readStructArrayPtrField(getset, i, PyGetSetDef__closure); + + PythonCextTypeBuiltins.addGetSet(pythonClass, dict, FromCharPointerNode.executeUncached(namePtr), getter, setter, doc, closure); } } + } - for (Object member : members) { - if (!PGuards.isNullOrZero(member, lib)) { - for (int i = 0;; i++) { - Object namePtr = readPointer.readStructArrayElement(member, i, PyMemberDef__name); - if (PGuards.isNullOrZero(namePtr, lib)) { - break; - } - TruffleString name = fromCharPointer.execute(namePtr); - int type = readI32.readStructArrayElement(member, i, PyMemberDef__type); - long offset = readI64.readStructArrayElement(member, i, PyMemberDef__offset); - int flags = readI32.readStructArrayElement(member, i, PyMemberDef__flags); - Object docPtr = readPointer.readStructArrayElement(member, i, PyMemberDef__doc); - Object doc = PGuards.isNullOrZero(docPtr, lib) ? PNone.NO_VALUE : fromCharPointer.execute(docPtr); - boolean canSet = (flags & CConstants.READONLY.intValue()) == 0; - addMember.execute(pythonClass, dict, name, type, offset, canSet ? 1 : 0, doc); + for (long member : members) { + if (member != NULLPTR) { + for (int i = 0;; i++) { + long namePtr = readStructArrayPtrField(member, i, PyMemberDef__name); + if (namePtr == NULLPTR) { + break; } + int type = readStructArrayIntField(member, i, PyMemberDef__type); + long offset = readStructArrayLongField(member, i, PyMemberDef__offset); + int flags = readStructArrayIntField(member, i, PyMemberDef__flags); + long docPtr = readStructArrayPtrField(member, i, PyMemberDef__doc); + Object doc = docPtr == NULLPTR ? PNone.NO_VALUE : FromCharPointerNode.executeUncached(docPtr); + boolean canSet = (flags & CConstants.READONLY.intValue()) == 0; + PythonCextTypeBuiltins.addMember(pythonClass, dict, FromCharPointerNode.executeUncached(namePtr), type, offset, canSet ? 1 : 0, doc); } } - return PNone.NO_VALUE; } + } - private static final int INDEX_GETSETS = 0; - private static final int INDEX_MEMBERS = 1; - - @TruffleBoundary - private static Object[] collect(MroSequenceStorage mro, int idx) { - ArrayList l = new ArrayList<>(); - int mroLength = mro.length(); - for (int i = 0; i < mroLength; i++) { - PythonAbstractClass kls = mro.getPythonClassItemNormalized(i); - Object value = HiddenAttr.ReadNode.executeUncached((PythonAbstractObject) kls, NATIVE_SLOTS, null); - if (value != null) { - Object[] tuple = (Object[]) value; - assert tuple.length == 2; - l.add(tuple[idx]); - } + private static final int INDEX_GETSETS = 0; + private static final int INDEX_MEMBERS = 1; + + @TruffleBoundary + private static long[] collect(MroSequenceStorage mro, int idx) { + int mroLength = mro.length(); + long[] result = new long[mroLength]; + int resultCount = 0; + for (int i = 0; i < mroLength; i++) { + PythonAbstractClass kls = mro.getPythonClassItemNormalized(i); + Object value = HiddenAttr.ReadNode.executeUncached((PythonAbstractObject) kls, NATIVE_SLOTS, null); + if (value != null) { + long[] tuple = (long[]) value; + assert tuple.length == 2; + result[resultCount++] = tuple[idx]; } - return l.toArray(); } + // the array may be overallocated, but the caller ignores any NULLPTR elements anyway + return result; } - @CApiBuiltin(ret = PyFrameObjectTransfer, args = {PyThreadState, PyCodeObject, PyObject, PyObject}, call = Direct) - abstract static class PyFrame_New extends CApiQuaternaryBuiltinNode { - @Specialization - static Object newFrame(Object threadState, PCode code, PythonObject globals, Object locals, - @Bind PythonLanguage language) { - Object frameLocals; - if (locals == null || PGuards.isPNone(locals)) { - frameLocals = PFactory.createDict(language); - } else { - frameLocals = locals; - } - return PFactory.createPFrame(language, threadState, code, globals, frameLocals); + @CApiBuiltin(ret = Void, args = {PyTypeObject}, call = Ignored) + public static void GraalPyPrivate_NotifyTypeReady(long pointer) { + CompilerAsserts.neverPartOfCompilation(); + Object object = NativeToPythonInternalNode.executeUncached(pointer, false); + if (object instanceof PythonNativeClass nativeClass) { + int nativeTypeId = CApiTransitions.createPythonNativeClassReference(nativeClass); + assert pointer == nativeClass.getPtr(); + CStructAccess.writeIntField(pointer, CFields.PyTypeObject__tp_version_tag, nativeTypeId); } } - @CApiBuiltin(ret = PyObjectTransfer, args = {Pointer, PyObject, Py_ssize_t, Int, Py_ssize_t, ConstCharPtrAsTruffleString, Int, Pointer, Pointer, Pointer, Pointer}, call = Ignored) - abstract static class GraalPyPrivate_MemoryViewFromBuffer extends CApi11BuiltinNode { + @CApiBuiltin(ret = PyFrameObjectRawPointer, args = {PyThreadState, PyCodeObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PyFrame_New(long threadState, long codePtr, long globalsPtr, long localsPtr) { + PCode code = (PCode) NativeToPythonNode.executeRawUncached(codePtr); + PythonObject globals = (PythonObject) NativeToPythonNode.executeRawUncached(globalsPtr); + Object locals = localsPtr == NULLPTR ? null : NativeToPythonNode.executeRawUncached(localsPtr); + PythonLanguage language = PythonLanguage.get(null); + Object frameLocals = locals == null || PGuards.isPNone(locals) ? PFactory.createDict(language) : locals; + return PythonToNativeNewRefNode.executeLongUncached(PFactory.createPFrame(language, threadState, code, globals, frameLocals)); + } - @Specialization - static Object wrap(Object bufferStructPointer, Object ownerObj, long lenObj, - Object readonlyObj, Object itemsizeObj, TruffleString format, - Object ndimObj, Object bufPointer, Object shapePointer, Object stridesPointer, Object suboffsetsPointer, - @Bind Node inliningTarget, - @Cached InlinedConditionProfile zeroDimProfile, - @Cached CStructAccess.ReadI64Node readShapeNode, - @Cached CStructAccess.ReadI64Node readStridesNode, - @Cached CStructAccess.ReadI64Node readSuboffsetsNode, - @Cached MemoryViewNodes.InitFlagsNode initFlagsNode, - @CachedLibrary(limit = "2") InteropLibrary lib, - @Cached CastToJavaIntExactNode castToIntNode, - @Cached TruffleString.CodePointLengthNode lengthNode, - @Cached TruffleString.CodePointAtIndexUTF32Node atIndexNode, - @Bind PythonLanguage language) { - int ndim = castToIntNode.execute(inliningTarget, ndimObj); - int itemsize = castToIntNode.execute(inliningTarget, itemsizeObj); - int len = castToIntNode.execute(inliningTarget, lenObj); - boolean readonly = castToIntNode.execute(inliningTarget, readonlyObj) != 0; - Object owner = PGuards.isNullOrZero(ownerObj, lib) ? null : ownerObj; - int[] shape = null; - int[] strides = null; - int[] suboffsets = null; - if (zeroDimProfile.profile(inliningTarget, ndim > 0)) { - if (!lib.isNull(shapePointer)) { - shape = readShapeNode.readLongAsIntArray(shapePointer, ndim); - } else { - assert ndim == 1; - shape = new int[]{len / itemsize}; - } - if (!lib.isNull(stridesPointer)) { - strides = readStridesNode.readLongAsIntArray(stridesPointer, ndim); - } else { - strides = PMemoryView.initStridesFromShape(ndim, itemsize, shape); - } - if (!lib.isNull(suboffsetsPointer)) { - suboffsets = readSuboffsetsNode.readLongAsIntArray(suboffsetsPointer, ndim); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {Pointer, PyObjectRawPointer, Py_ssize_t, Int, Py_ssize_t, ConstCharPtr, Int, Pointer, Pointer, Pointer, Pointer}, call = Ignored) + static long GraalPyPrivate_MemoryViewFromBuffer(long bufferStructPointer, long ownerPtr, long lenArg, int readonlyArg, long itemsizeArg, long formatPtr, int ndim, long bufPointer, + long shapePointer, long stridesPointer, long suboffsetsPointer) { + int itemsize = CastToJavaIntExactNode.executeUncached(itemsizeArg); + int len = CastToJavaIntExactNode.executeUncached(lenArg); + boolean readonly = readonlyArg != 0; + Object owner = ownerPtr == NULLPTR ? null : NativeToPythonNode.executeRawUncached(ownerPtr); + TruffleString format = (TruffleString) CharPtrToPythonNode.getUncached().execute(formatPtr); + int[] shape = null; + int[] strides = null; + int[] suboffsets = null; + if (ndim > 0) { + if (shapePointer != NULLPTR) { + shape = readLongArrayElementsAsInts(shapePointer, ndim); + } else { + assert ndim == 1; + shape = new int[]{len / itemsize}; } - Object buffer = NativeByteSequenceStorage.create(bufPointer, len, len, false); - int flags = initFlagsNode.execute(inliningTarget, ndim, itemsize, shape, strides, suboffsets); - BufferLifecycleManager bufferLifecycleManager = null; - if (!lib.isNull(bufferStructPointer)) { - bufferLifecycleManager = new NativeBufferLifecycleManager.NativeBufferLifecycleManagerFromType(bufferStructPointer); + if (stridesPointer != NULLPTR) { + strides = readLongArrayElementsAsInts(stridesPointer, ndim); + } else { + strides = PMemoryView.initStridesFromShape(ndim, itemsize, shape); + } + if (suboffsetsPointer != NULLPTR) { + suboffsets = readLongArrayElementsAsInts(suboffsetsPointer, ndim); } - return PFactory.createMemoryView(language, PythonContext.get(inliningTarget), bufferLifecycleManager, buffer, owner, len, readonly, itemsize, - BufferFormat.forMemoryView(format, lengthNode, atIndexNode), format, ndim, bufPointer, 0, shape, strides, suboffsets, flags); } + Object buffer = NativeByteSequenceStorage.create(bufPointer, len, len, false); + int flags = MemoryViewNodes.InitFlagsNode.executeUncached(ndim, itemsize, shape, strides, suboffsets); + BufferLifecycleManager bufferLifecycleManager = null; + if (bufferStructPointer != NULLPTR) { + bufferLifecycleManager = new NativeBufferLifecycleManager.NativeBufferLifecycleManagerFromType(bufferStructPointer); + } + Object memoryView = PFactory.createMemoryView(PythonLanguage.get(null), PythonContext.get(null), bufferLifecycleManager, buffer, owner, len, readonly, itemsize, + BufferFormat.forMemoryView(format, TruffleString.CodePointLengthNode.getUncached(), TruffleString.CodePointAtIndexUTF32Node.getUncached()), format, ndim, bufPointer, 0, + shape, strides, suboffsets, flags); + return PythonToNativeNewRefNode.executeLongUncached(memoryView); + } + + private static int[] readLongArrayElementsAsInts(long pointer, int elements) { + int[] result = new int[elements]; + for (int i = 0; i < result.length; i++) { + result[i] = (int) readLongArrayElement(pointer, i); + } + return result; } @GenerateInline @@ -1224,11 +1125,17 @@ static Object[] doNotNull(VirtualFrame frame, Object args, @GenerateInline @GenerateCached(false) + @GenerateUncached @ReportPolymorphism abstract static class CastKwargsNode extends PNodeWithContext { public abstract PKeyword[] execute(Node inliningTarget, Object kwargsObj); + @TruffleBoundary + public static PKeyword[] executeUncached(Object kwargsObj) { + return PythonCextBuiltinsFactory.CastKwargsNodeGen.getUncached().execute(null, kwargsObj); + } + @Specialization(guards = "isNoValue(kwargs)") @SuppressWarnings("unused") static PKeyword[] doNoKeywords(Object kwargs) { @@ -1243,138 +1150,86 @@ static PKeyword[] doKeywords(Node inliningTarget, Object kwargs, } @CApiBuiltin(ret = SIZE_T, args = {}, call = Ignored) - abstract static class GraalPyPrivate_GetMaxNativeMemory extends CApiNullaryBuiltinNode { - @Specialization - @TruffleBoundary - long get() { - return PythonOptions.MaxNativeMemory.getValue(getContext().getEnv().getOptions()); - } + @TruffleBoundary + static long GraalPyPrivate_GetMaxNativeMemory() { + return PythonOptions.MaxNativeMemory.getValue(PythonContext.get(null).getEnv().getOptions()); } @CApiBuiltin(ret = SIZE_T, args = {}, call = Ignored) - abstract static class GraalPyPrivate_GetInitialNativeMemory extends CApiNullaryBuiltinNode { - @Specialization - @TruffleBoundary - long get() { - return PythonOptions.InitialNativeMemory.getValue(getContext().getEnv().getOptions()); - } + @TruffleBoundary + static long GraalPyPrivate_GetInitialNativeMemory() { + return PythonOptions.InitialNativeMemory.getValue(PythonContext.get(null).getEnv().getOptions()); } - @CApiBuiltin(ret = Void, args = {SIZE_T}, call = Ignored) - abstract static class GraalPyPrivate_TriggerGC extends CApiUnaryBuiltinNode { + // doesn't need a GIL; just accessing thread-local data + @CApiBuiltin(ret = Int, args = {PyThreadState, SIZE_T}, call = Ignored, acquireGil = false) + @TruffleBoundary + static int GraalPyPrivate_DeallocStack_Grow(long threadStatePointer, long newCapacity) { + return PThreadState.growDeallocatingStack(threadStatePointer, newCapacity); + } - @Specialization - @TruffleBoundary - Object trigger(long delay) { - LOGGER.fine("full GC due to native memory"); - PythonUtils.forceFullGC(); - try { - Thread.sleep(delay); - } catch (InterruptedException x) { - // Restore interrupt status - Thread.currentThread().interrupt(); - } - CApiTransitions.pollReferenceQueue(); - PythonContext.triggerAsyncActions(this); - return PNone.NO_VALUE; - } + @CApiBuiltin(ret = Void, args = {SIZE_T}, call = Ignored) + @TruffleBoundary + static void GraalPyPrivate_TriggerGC(long delay) { + LOGGER.fine("full GC due to native memory"); + PythonUtils.forceFullGC(); + try { + Thread.sleep(delay); + } catch (InterruptedException x) { + // Restore interrupt status + Thread.currentThread().interrupt(); + } + CApiTransitions.pollReferenceQueue(); + PythonContext.triggerAsyncActions(EncapsulatingNodeReference.getCurrent().get()); } @CApiBuiltin(ret = Void, args = {Pointer}, call = Ignored) - abstract static class GraalPyPrivate_ManagedObject_GC_Del extends CApiUnaryBuiltinNode { - - @Specialization(limit = "3") - static PNone doObject(Object ptr, - @Bind Node inliningTarget, - @Cached PyObjectGCDelNode pyObjectGCDelNode, - @CachedLibrary("ptr") InteropLibrary lib) { - // we expect a pointer object here because this is called from native - assert CApiTransitions.isBackendPointerObject(ptr); - if (lib.isPointer(ptr)) { - try { - pyObjectGCDelNode.execute(inliningTarget, lib.asPointer(ptr)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - return PNone.NO_VALUE; - } + static void GraalPyPrivate_ManagedObject_GC_Del(long ptr) { + PyObjectGCDelNode.executeUncached(ptr); } @CApiBuiltin(ret = Void, args = {UNSIGNED_INT, UINTPTR_T, SIZE_T}, call = Ignored) - abstract static class GraalPyPrivate_TraceMalloc_Track extends CApiTernaryBuiltinNode { - private static final TruffleLogger LOGGER = CApiContext.getLogger(GraalPyPrivate_TraceMalloc_Track.class); - - @Specialization - @TruffleBoundary - static Object doCachedDomainIdx(int domain, long ptrVal, long size) { - // this will also be called if the allocation failed - if (ptrVal != 0) { - LOGGER.fine(() -> PythonUtils.formatJString("Tracking memory (domain: %d, size: %d): %s", domain, size, CApiContext.asHex(ptrVal))); - } - return PNone.NO_VALUE; + @TruffleBoundary + static void GraalPyPrivate_TraceMalloc_Track(int domain, long ptrVal, long size) { + // this will also be called if the allocation failed + if (ptrVal != 0) { + LOGGER.fine(() -> PythonUtils.formatJString("Tracking memory (domain: %d, size: %d): %s", domain, size, CApiContext.asHex(ptrVal))); } } @CApiBuiltin(ret = Void, args = {UNSIGNED_INT, UINTPTR_T}, call = Ignored) - abstract static class GraalPyPrivate_TraceMalloc_Untrack extends CApiBinaryBuiltinNode { - private static final TruffleLogger LOGGER = CApiContext.getLogger(GraalPyPrivate_TraceMalloc_Untrack.class); - - @Specialization - @TruffleBoundary - Object doCachedDomainIdx(int domain, long ptrVal) { - LOGGER.fine(() -> PythonUtils.formatJString("Untracking memory (domain: %d): %s", domain, CApiContext.asHex(ptrVal))); - return PNone.NO_VALUE; - } + @TruffleBoundary + static void GraalPyPrivate_TraceMalloc_Untrack(int domain, long ptrVal) { + LOGGER.fine(() -> PythonUtils.formatJString("Untracking memory (domain: %d): %s", domain, CApiContext.asHex(ptrVal))); } - @GenerateCached(false) - abstract static class GraalPyPrivate_GcTracingNode extends CApiUnaryBuiltinNode { - - @Specialization(guards = "!traceMem(language)") - static Object doNothing(@SuppressWarnings("unused") Object ptr, - @SuppressWarnings("unused") @Bind PythonLanguage language) { - // do nothing - return PNone.NO_VALUE; - } - - @Fallback - Object doNativeWrapper(Object ptr, - @Bind Node inliningTarget, - @Bind PythonContext context, - @Cached GetCurrentFrameRef getCurrentFrameRef, - @CachedLibrary(limit = "3") InteropLibrary lib) { - PFrame.Reference ref = null; - if (context.getOption(PythonOptions.TraceNativeMemoryCalls)) { - ref = getCurrentFrameRef.execute(null, inliningTarget); - } - trace(context, CApiContext.asPointer(ptr, lib), ref, null); - return PNone.NO_VALUE; - } - - @Idempotent - boolean traceMem(PythonLanguage language) { - return language.getEngineOption(PythonOptions.TraceNativeMemory); - } - - protected abstract void trace(PythonContext context, Object ptr, Reference ref, TruffleString className); + @CApiBuiltin(ret = Void, args = {Pointer}, call = Ignored) + static void GraalPyPrivate_Object_GC_UnTrack(long ptr) { + traceNativeGCObject(ptr, false); } @CApiBuiltin(ret = Void, args = {Pointer}, call = Ignored) - abstract static class GraalPyPrivate_Object_GC_UnTrack extends GraalPyPrivate_GcTracingNode { - @Override - protected void trace(PythonContext context, Object ptr, Reference ref, TruffleString className) { - GC_LOGGER.finer(() -> PythonUtils.formatJString("Untracking container object at %s", CApiContext.asHex(ptr))); - context.getCApiContext().untrackObject(ptr, ref, className); - } + static void GraalPyPrivate_Object_GC_Track(long ptr) { + traceNativeGCObject(ptr, true); } - @CApiBuiltin(ret = Void, args = {Pointer}, call = Ignored) - abstract static class GraalPyPrivate_Object_GC_Track extends GraalPyPrivate_GcTracingNode { - @Override - protected void trace(PythonContext context, Object ptr, Reference ref, TruffleString className) { + private static void traceNativeGCObject(long ptr, boolean track) { + PythonLanguage language = PythonLanguage.get(null); + if (!language.getEngineOption(PythonOptions.TraceNativeMemory)) { + return; + } + + PythonContext context = PythonContext.get(null); + Reference ref = null; + if (context.getOption(PythonOptions.TraceNativeMemoryCalls)) { + ref = GetCurrentFrameRef.executeUncached(); + } + if (track) { GC_LOGGER.finer(() -> PythonUtils.formatJString("Tracking container object at %s", CApiContext.asHex(ptr))); - context.getCApiContext().trackObject(ptr, ref, className); + context.getCApiContext().trackObject(ptr, ref, null); + } else { + GC_LOGGER.finer(() -> PythonUtils.formatJString("Untracking container object at %s", CApiContext.asHex(ptr))); + context.getCApiContext().untrackObject(ptr, ref, null); } } @@ -1403,110 +1258,95 @@ protected void trace(PythonContext context, Object ptr, Reference ref, TruffleSt * object) and then stored in a Java object array which is then attached to the primary object. *

*/ - @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 boolean capsuleNameMatches(long name1, long name2) { + if (name1 == NULLPTR || name2 == NULLPTR) { + return name1 == name2; } - - @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_SetName"); + if (name1 == name2) { + return true; } - } - - @CApiBuiltin(ret = Int, args = {PyObject, PY_CAPSULE_DESTRUCTOR}, call = Direct) - abstract static class PyCapsule_SetDestructor extends CApiBinaryBuiltinNode { - @Specialization - static int doCapsule(PyCapsule o, Object destructor, - @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_SetDestructor"); + for (int i = 0;; i++) { + byte b1 = readByteArrayElement(name1, i); + byte b2 = readByteArrayElement(name2, i); + if (b1 != b2) { + return false; } - o.registerDestructor(lib.isNull(destructor) ? null : destructor); - return 0; - } - - @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_SetDestructor"); - } - } - - @CApiBuiltin(ret = Int, args = {PyObject, Pointer}, call = Direct) - abstract static class PyCapsule_SetContext extends CApiBinaryBuiltinNode { - @Specialization - static int doCapsule(PyCapsule o, Object context, - @Bind Node inliningTarget, - @Cached PRaiseNode raiseNode) { - if (o.getPointer() == null) { - throw raiseNode.raise(inliningTarget, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, "PyCapsule_SetContext"); + if (b1 == 0) { + return true; } - o.setContext(context); - return 0; - } - - @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_SetContext"); } } - @CApiBuiltin(ret = Pointer, args = {ConstCharPtr, Int}, call = Direct) - abstract static class PyCapsule_Import extends CApiBinaryBuiltinNode { - @Specialization - static Object doGeneric(Object namePtr, @SuppressWarnings("unused") int noBlock, - @Bind Node inliningTarget, - @Cached CApiTransitions.CharPtrToPythonNode charPtrToPythonNode, - @Cached PyCapsuleNameMatchesNode nameMatchesNode, - @Cached TruffleString.CodePointLengthNode codePointLengthNode, - @Cached TruffleString.IndexOfStringNode indexOfStringNode, - @Cached TruffleString.SubstringNode substringNode, - @Cached ReadAttributeFromObjectNode getAttrNode, - @Cached PRaiseNode raiseNode) { - TruffleString name = (TruffleString) charPtrToPythonNode.execute(namePtr); - TruffleString trace = name; - Object object = null; - while (trace != null) { - int traceLen = codePointLengthNode.execute(trace, TS_ENCODING); - int dotIdx = indexOfStringNode.execute(trace, StringLiterals.T_DOT, 0, traceLen, TS_ENCODING); - TruffleString dot = null; - if (dotIdx >= 0) { - dot = substringNode.execute(trace, dotIdx + 1, traceLen - dotIdx - 1, TS_ENCODING, false); - trace = substringNode.execute(trace, 0, dotIdx, TS_ENCODING, false); - } - if (object == null) { - // noBlock has no effect anymore since 3.3 - object = AbstractImportNode.importModuleBoundary(trace); - } else { - object = getAttrNode.execute(object, trace); - } - trace = dot; - } - - /* compare attribute name to module.name by hand */ - PyCapsule capsule = object instanceof PyCapsule ? (PyCapsule) object : null; - if (capsule != null && PyCapsule_IsValid.doCapsule(capsule, namePtr, inliningTarget, nameMatchesNode) == 1) { - return capsule.getPointer(); - } else { - throw raiseNode.raise(inliningTarget, AttributeError, PY_CAPSULE_IMPORT_S_IS_NOT_VALID, name); - } + private static PyCapsule expectCapsule(long oPtr, String builtinName) { + Object obj = NativeToPythonNode.executeRawUncached(oPtr); + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, !(obj instanceof PyCapsule capsule) || capsule.getPointer() == NULLPTR)) { + throw PRaiseNode.raiseStatic(null, ValueError, CALLED_WITH_INVALID_PY_CAPSULE_OBJECT, builtinName); } + return (PyCapsule) obj; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextClassBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextClassBuiltins.java index f6639599a9..b296f7dcb6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextClassBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextClassBuiltins.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 @@ -40,49 +40,36 @@ */ package com.oracle.graal.python.builtins.modules.cext; +import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltinNode.checkNonNullArgUncached; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; -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 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.CApiUnaryBuiltinNode; import com.oracle.graal.python.builtins.objects.method.PDecoratedMethod; -import com.oracle.graal.python.nodes.PRaiseNode; +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.runtime.object.PFactory; -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; public final class PythonCextClassBuiltins { - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyInstanceMethod_New extends CApiUnaryBuiltinNode { - @Specialization - static Object staticmethod(Object func, - @Bind Node inliningTarget, - @Bind PythonLanguage language, - @Cached PRaiseNode raiseNode) { - checkNonNullArg(inliningTarget, func, raiseNode); - PDecoratedMethod res = PFactory.createInstancemethod(language); - res.setCallable(func); - return res; - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct) + static long PyInstanceMethod_New(long funcPtr) { + Object func = NativeToPythonNode.executeRawUncached(funcPtr); + checkNonNullArgUncached(func); + PDecoratedMethod res = PFactory.createInstancemethod(PythonLanguage.get(null)); + res.setCallable(func); + return PythonToNativeNewRefNode.executeLongUncached(res); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct) - abstract static class PyMethod_New extends CApiBinaryBuiltinNode { - @Specialization - static Object methodNew(Object func, Object self, - @Bind Node inliningTarget, - @Bind PythonLanguage language, - @Cached PRaiseNode raiseNode) { - checkNonNullArg(inliningTarget, func, self, raiseNode); - // Note: CPython also constructs the object directly, without running the constructor or - // checking the inputs - return PFactory.createMethod(language, self, func); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PyMethod_New(long funcPtr, long selfPtr) { + Object func = NativeToPythonNode.executeRawUncached(funcPtr); + Object self = NativeToPythonNode.executeRawUncached(selfPtr); + checkNonNullArgUncached(func); + checkNonNullArgUncached(self); + // Note: CPython also constructs the object directly, without running the constructor or + // checking the inputs. + return PythonToNativeNewRefNode.executeLongUncached(PFactory.createMethod(PythonLanguage.get(null), self, func)); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodeBuiltins.java index e12cf3dcdf..0b20c25166 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodeBuiltins.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,99 +41,91 @@ package com.oracle.graal.python.builtins.modules.cext; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; -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.ConstCharPtr; 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.PyCodeObject; -import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyCodeObjectTransfer; -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.PyCodeObjectRawPointer; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectRawPointer; import static com.oracle.graal.python.util.PythonUtils.EMPTY_BYTE_ARRAY; import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY; import static com.oracle.graal.python.util.PythonUtils.EMPTY_TRUFFLESTRING_ARRAY; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi18BuiltinNode; -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.objects.code.CodeNodes; import com.oracle.graal.python.builtins.objects.code.PCode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode; +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.call.CallNode; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.strings.TruffleString; public final class PythonCextCodeBuiltins { - @CApiBuiltin(ret = PyCodeObjectTransfer, args = {Int, Int, Int, Int, Int, Int, PyObject, PyObject, PyObject, PyObject, PyObject, PyObject, PyObject, PyObject, PyObject, Int, PyObject, - PyObject}, call = Direct) - abstract static class PyUnstable_Code_NewWithPosOnlyArgs extends CApi18BuiltinNode { - @Specialization - @TruffleBoundary - public static Object codeNew(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, Object code, Object consts, - Object names, Object varnames, Object freevars, Object cellvars, - Object filename, Object name, Object qualname, - int firstlineno, Object lnotab, - @SuppressWarnings("unused") Object exceptionTable, - @Cached CallNode callNode) { - /* - * This rearranges the arguments (freevars, cellvars). - */ - Object[] args = new Object[]{ - argcount, - posonlyargcount, - kwonlyargcount, nlocals, stacksize, flags, - code, consts, names, varnames, - filename, name, qualname, - firstlineno, lnotab, exceptionTable, - freevars, cellvars - }; - return callNode.executeWithoutFrame(PythonBuiltinClassType.PCode, args); - } + @CApiBuiltin(ret = PyCodeObjectRawPointer, args = {Int, Int, Int, Int, Int, Int, PyObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer, + PyObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer, PyObjectRawPointer, Int, PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PyUnstable_Code_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, long codePtr, long constsPtr, + long namesPtr, long varnamesPtr, long freevarsPtr, long cellvarsPtr, + long filenamePtr, long namePtr, long qualnamePtr, + int firstlineno, long lnotabPtr, long exceptionTablePtr) { + Object code = NativeToPythonNode.executeRawUncached(codePtr); + Object consts = NativeToPythonNode.executeRawUncached(constsPtr); + Object names = NativeToPythonNode.executeRawUncached(namesPtr); + Object varnames = NativeToPythonNode.executeRawUncached(varnamesPtr); + Object freevars = NativeToPythonNode.executeRawUncached(freevarsPtr); + Object cellvars = NativeToPythonNode.executeRawUncached(cellvarsPtr); + Object filename = NativeToPythonNode.executeRawUncached(filenamePtr); + Object name = NativeToPythonNode.executeRawUncached(namePtr); + Object qualname = NativeToPythonNode.executeRawUncached(qualnamePtr); + Object lnotab = NativeToPythonNode.executeRawUncached(lnotabPtr); + Object exceptionTable = NativeToPythonNode.executeRawUncached(exceptionTablePtr); + /* + * This rearranges the arguments (freevars, cellvars). + */ + Object[] args = new Object[]{ + argcount, + posonlyargcount, + kwonlyargcount, nlocals, stacksize, flags, + code, consts, names, varnames, + filename, name, qualname, + firstlineno, lnotab, exceptionTable, + freevars, cellvars + }; + return PythonToNativeNewRefNode.executeLongUncached(CallNode.executeUncached(PythonBuiltinClassType.PCode, args)); } - @CApiBuiltin(ret = PyCodeObjectTransfer, args = {ConstCharPtrAsTruffleString, ConstCharPtrAsTruffleString, Int}, call = Direct) - abstract static class PyCode_NewEmpty extends CApiTernaryBuiltinNode { - public abstract PCode execute(TruffleString filename, TruffleString funcname, int lineno); + @CApiBuiltin(ret = PyCodeObjectRawPointer, args = {ConstCharPtr, ConstCharPtr, Int}, call = Direct) + static long PyCode_NewEmpty(long filenamePtr, long funcnamePtr, int lineno) { + TruffleString filename = (TruffleString) CharPtrToPythonNode.getUncached().execute(filenamePtr); + TruffleString funcname = (TruffleString) CharPtrToPythonNode.getUncached().execute(funcnamePtr); + return PythonToNativeNewRefNode.executeLongUncached(createCodeNewEmpty(filename, funcname, lineno)); + } - @Specialization - @TruffleBoundary - static PCode newEmpty(TruffleString filename, TruffleString funcname, int lineno, - @Cached CodeNodes.CreateCodeNode createCodeNode) { - return createCodeNode.execute(null, 0, 0, 0, 0, 0, 0, - EMPTY_BYTE_ARRAY, EMPTY_OBJECT_ARRAY, EMPTY_TRUFFLESTRING_ARRAY, - EMPTY_TRUFFLESTRING_ARRAY, EMPTY_TRUFFLESTRING_ARRAY, EMPTY_TRUFFLESTRING_ARRAY, - filename, funcname, funcname, - lineno, EMPTY_BYTE_ARRAY); + @CApiBuiltin(ret = Int, args = {PyCodeObjectRawPointer, Int}, call = Direct) + static int PyCode_Addr2Line(long codePtr, int lasti) { + PCode code = (PCode) NativeToPythonNode.executeRawUncached(codePtr); + if (lasti < 0) { + return code.co_firstlineno(); } + return code.lastiToLine(lasti); } - @CApiBuiltin(ret = Int, args = {PyCodeObject, Int}, call = Direct) - abstract static class PyCode_Addr2Line extends CApiBinaryBuiltinNode { - @Specialization - static int addr2line(PCode code, int lasti) { - if (lasti < 0) { - return code.co_firstlineno(); - } - return code.lastiToLine(lasti); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyCodeObjectRawPointer}, call = Direct) + static long GraalPyCode_GetName(long codePtr) { + PCode code = (PCode) NativeToPythonNode.executeRawUncached(codePtr); + return PythonToNativeNewRefNode.executeLongUncached(code.getName()); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyCodeObject}, call = Direct) - abstract static class GraalPyCode_GetName extends CApiUnaryBuiltinNode { - @Specialization - static Object get(PCode code) { - return code.getName(); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyCodeObjectRawPointer}, call = Direct) + static long GraalPyCode_GetFileName(long codePtr) { + PCode code = (PCode) NativeToPythonNode.executeRawUncached(codePtr); + return PythonToNativeNewRefNode.executeLongUncached(code.getFilename()); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyCodeObject}, call = Direct) - abstract static class GraalPyCode_GetFileName extends CApiUnaryBuiltinNode { - @Specialization - static Object get(PCode code) { - return code.getFilename(); - } + static PCode createCodeNewEmpty(TruffleString filename, TruffleString funcname, int lineno) { + return CodeNodes.CreateCodeNode.executeUncached(0, 0, 0, 0, 0, 0, + EMPTY_BYTE_ARRAY, EMPTY_OBJECT_ARRAY, EMPTY_TRUFFLESTRING_ARRAY, + EMPTY_TRUFFLESTRING_ARRAY, EMPTY_TRUFFLESTRING_ARRAY, EMPTY_TRUFFLESTRING_ARRAY, + filename, funcname, funcname, + lineno, EMPTY_BYTE_ARRAY); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodecBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodecBuiltins.java index 2dd3d94780..b270c93024 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodecBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCodecBuiltins.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,42 +41,30 @@ package com.oracle.graal.python.builtins.modules.cext; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; -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.PyObjectTransfer; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtr; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectRawPointer; import com.oracle.graal.python.builtins.modules.CodecsModuleBuiltins; 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.common.SequenceStorageNodes; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNewRefNode; import com.oracle.graal.python.builtins.objects.tuple.PTuple; -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 PythonCextCodecBuiltins { - @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtrAsTruffleString}, call = Direct) - abstract static class PyCodec_Encoder extends CApiUnaryBuiltinNode { - @Specialization - Object get(TruffleString encoding, - @Bind Node inliningTarget, - @Cached CodecsModuleBuiltins.PyCodecLookupNode lookupNode, - @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode) { - PTuple codecInfo = lookupNode.execute(null, inliningTarget, encoding); - return getItemScalarNode.execute(inliningTarget, codecInfo.getSequenceStorage(), 0); - } + + @CApiBuiltin(ret = PyObjectRawPointer, args = {ConstCharPtr}, call = Direct) + static long PyCodec_Encoder(long encodingPtr) { + TruffleString encoding = (TruffleString) CharPtrToPythonNode.getUncached().execute(encodingPtr); + PTuple codecInfo = CodecsModuleBuiltins.PyCodecLookupNode.executeUncached(encoding); + return PythonToNativeNewRefNode.executeLongUncached(SequenceStorageNodes.GetItemScalarNode.executeUncached(codecInfo.getSequenceStorage(), 0)); } - @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtrAsTruffleString}, call = Direct) - abstract static class PyCodec_Decoder extends CApiUnaryBuiltinNode { - @Specialization - Object get(TruffleString encoding, - @Bind Node inliningTarget, - @Cached CodecsModuleBuiltins.PyCodecLookupNode lookupNode, - @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode) { - PTuple codecInfo = lookupNode.execute(null, inliningTarget, encoding); - return getItemScalarNode.execute(inliningTarget, codecInfo.getSequenceStorage(), 1); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {ConstCharPtr}, call = Direct) + static long PyCodec_Decoder(long encodingPtr) { + TruffleString encoding = (TruffleString) CharPtrToPythonNode.getUncached().execute(encodingPtr); + PTuple codecInfo = CodecsModuleBuiltins.PyCodecLookupNode.executeUncached(encoding); + return PythonToNativeNewRefNode.executeLongUncached(SequenceStorageNodes.GetItemScalarNode.executeUncached(codecInfo.getSequenceStorage(), 1)); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextComplexBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextComplexBuiltins.java index f6241cff8c..ce73b60f22 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextComplexBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextComplexBuiltins.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,7 +46,8 @@ 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.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.builtins.objects.cext.structs.CStructAccess.writeDoubleField; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___FLOAT__; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; @@ -54,13 +55,14 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; 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.CApiUnaryBuiltinNode; 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.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.complex.ComplexBuiltins; import com.oracle.graal.python.builtins.objects.complex.PComplex; +import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.call.CallNode; @@ -69,102 +71,64 @@ import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; -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.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; public final class PythonCextComplexBuiltins { + // TODO(CAPI STATIC): uses nodes without @GenerateUncached @CApiBuiltin(ret = Int, args = {PyObject, Pointer}, call = Ignored) abstract static class GraalPyPrivate_Complex_AsCComplex extends CApiBinaryBuiltinNode { @Specialization - static int asComplex(PComplex c, Object out, - @Shared @Cached CStructAccess.WriteDoubleNode writeDoubleNode) { - writeDoubleNode.write(out, CFields.Py_complex__real, c.getReal()); - writeDoubleNode.write(out, CFields.Py_complex__imag, c.getImag()); + static int asComplex(PComplex c, long out) { + writeDoubleField(out, CFields.Py_complex__real, c.getReal()); + writeDoubleField(out, CFields.Py_complex__imag, c.getImag()); return 0; } @Specialization(guards = "!isPComplex(obj)") - static int doGeneric(Object obj, Object out, - @Cached ComplexBuiltins.ComplexNewNode complexNode, - @Shared @Cached CStructAccess.WriteDoubleNode writeDoubleNode) { + static int doGeneric(Object obj, long out, + @Cached ComplexBuiltins.ComplexNewNode complexNode) { PComplex c = (PComplex) complexNode.execute(null, PythonBuiltinClassType.PComplex, obj, PNone.NO_VALUE); - writeDoubleNode.write(out, CFields.Py_complex__real, c.getReal()); - writeDoubleNode.write(out, CFields.Py_complex__imag, c.getImag()); + writeDoubleField(out, CFields.Py_complex__real, c.getReal()); + writeDoubleField(out, CFields.Py_complex__imag, c.getImag()); return 0; } } - @CApiBuiltin(ret = ArgDescriptor.Double, args = {PyObject}, call = Ignored) - @ImportStatic(PythonCextComplexBuiltins.class) - abstract static class GraalPyPrivate_Complex_RealAsDouble extends CApiUnaryBuiltinNode { + public static final TruffleString T_REAL = tsLiteral("real"); - public static final TruffleString T_REAL = tsLiteral("real"); - - @Specialization - static double asDouble(PComplex d) { - return d.getReal(); + @CApiBuiltin(ret = ArgDescriptor.Double, args = {PyObjectRawPointer}, call = Ignored) + static double GraalPyPrivate_Complex_RealAsDouble(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + if (obj instanceof PComplex complex) { + return complex.getReal(); } - - @Specialization(guards = "!isPComplex(obj)") - static Object asDouble(Object obj, - @Bind Node inliningTarget, - @Cached PyObjectGetAttr getAttr, - @Cached CallNode callNode, - @Cached BuiltinClassProfiles.IsBuiltinObjectProfile isBuiltinObjectProfile, - @Cached PRaiseNode raiseNode) { - TruffleString name; - if (isBuiltinObjectProfile.profileObject(inliningTarget, obj, PythonBuiltinClassType.PComplex)) { - name = T_REAL; - } else { - name = T___FLOAT__; - } - try { - return callNode.executeWithoutFrame(getAttr.execute(null, inliningTarget, obj, name)); - } catch (PException e) { - throw raiseNode.raise(inliningTarget, TypeError); - } + TruffleString name = BuiltinClassProfiles.IsBuiltinObjectProfile.getUncached().profileObject(null, obj, PythonBuiltinClassType.PComplex) ? T_REAL : T___FLOAT__; + try { + return PyFloatAsDoubleNode.executeUncached(CallNode.executeUncached(PyObjectGetAttr.executeUncached(obj, name))); + } catch (PException e) { + throw PRaiseNode.raiseStatic(null, TypeError); } } - @CApiBuiltin(ret = ArgDescriptor.Double, args = {PyObject}, call = Ignored) - @ImportStatic(PythonCextComplexBuiltins.class) - abstract static class GraalPyPrivate_Complex_ImagAsDouble extends CApiUnaryBuiltinNode { - - public static final TruffleString T_IMAG = tsLiteral("imag"); + public static final TruffleString T_IMAG = tsLiteral("imag"); - @Specialization - static double asDouble(PComplex d) { - return d.getImag(); + @CApiBuiltin(ret = ArgDescriptor.Double, args = {PyObjectRawPointer}, call = Ignored) + static double GraalPyPrivate_Complex_ImagAsDouble(long objPtr) { + Object obj = NativeToPythonNode.executeRawUncached(objPtr); + if (obj instanceof PComplex complex) { + return complex.getImag(); } - - @Specialization(guards = "!isPComplex(obj)") - static Object asDouble(Object obj, - @Bind Node inliningTarget, - @Cached PyObjectGetAttr getAttr, - @Cached CallNode callNode, - @Cached GetClassNode getClassNode, - @Cached IsSubtypeNode isSubtypeNode) { - if (isSubtypeNode.execute(getClassNode.execute(inliningTarget, obj), PythonBuiltinClassType.PComplex)) { - return callNode.executeWithoutFrame(getAttr.execute(null, inliningTarget, obj, T_IMAG)); - } else { - return 0.0; - } + if (IsSubtypeNode.getUncached().execute(GetClassNode.executeUncached(obj), PythonBuiltinClassType.PComplex)) { + return PyFloatAsDoubleNode.executeUncached(CallNode.executeUncached(PyObjectGetAttr.executeUncached(obj, T_IMAG))); } + return 0.0; } - @CApiBuiltin(ret = PyObjectTransfer, args = {ArgDescriptor.Double, ArgDescriptor.Double}, call = Direct) - abstract static class PyComplex_FromDoubles extends CApiBinaryBuiltinNode { - - @Specialization - static PComplex asDouble(double r, double i, - @Bind PythonLanguage language) { - return PFactory.createComplex(language, r, i); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {ArgDescriptor.Double, ArgDescriptor.Double}, call = Direct) + static long PyComplex_FromDoubles(double r, double i) { + return PythonToNativeNewRefNode.executeLongUncached(PFactory.createComplex(PythonLanguage.get(null), r, i)); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextContextBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextContextBuiltins.java index 90f8edd12e..63e70c3926 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextContextBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextContextBuiltins.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 @@ -42,23 +42,22 @@ import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored; -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.ConstCharPtr; 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.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.builtins.objects.cext.capi.transitions.ArgDescriptor.VoidNoReturn; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.runtime.exception.ExceptionUtils.printPythonLikeStackTrace; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -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.CApiNullaryBuiltinNode; -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.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.PRaiseNativeNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode; +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.contextvars.PContextVar; import com.oracle.graal.python.builtins.objects.contextvars.PContextVarsContext; import com.oracle.graal.python.lib.PyContextCopyCurrent; @@ -68,135 +67,91 @@ import com.oracle.graal.python.runtime.PythonContext; 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.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 PythonCextContextBuiltins { @CApiBuiltin(ret = VoidNoReturn, args = {}, call = Ignored) - abstract static class GraalPyPrivate_PrintStacktrace extends CApiNullaryBuiltinNode { - - @Specialization - @TruffleBoundary - static Object stacktrace() { - printPythonLikeStackTrace(); - return 0; - } + @TruffleBoundary + static void GraalPyPrivate_PrintStacktrace() { + printPythonLikeStackTrace(); } - @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtrAsTruffleString, PyObject}, call = Direct) - abstract static class PyContextVar_New extends CApiBinaryBuiltinNode { - @Specialization - static Object doGeneric(TruffleString name, Object def, - @Cached CallNode callContextvar) { - return callContextvar.executeWithoutFrame(PythonBuiltinClassType.ContextVar, name, def); - } - - @Specialization - static Object doGeneric(PNone name, @SuppressWarnings("unused") Object def) { - assert name == PNone.NO_VALUE; - return PNone.NO_VALUE; + @CApiBuiltin(ret = PyObjectRawPointer, args = {ConstCharPtr, PyObjectRawPointer}, call = Direct) + static long PyContextVar_New(long namePtr, long defPtr) { + if (namePtr == NULLPTR) { + return NULLPTR; } + TruffleString name = (TruffleString) CharPtrToPythonNode.getUncached().execute(namePtr); + Object def = defPtr == NULLPTR ? PNone.NO_VALUE : NativeToPythonNode.executeRawUncached(defPtr); + return PythonToNativeNewRefNode.executeLongUncached(CallNode.executeUncached(PythonBuiltinClassType.ContextVar, name, def)); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, Pointer}, call = Ignored) - abstract static class GraalPyPrivate_ContextVar_Get extends CApiTernaryBuiltinNode { - @Specialization - static Object doGeneric(Object var, Object def, Object marker, - @Bind Node inliningTarget, - @Bind PythonContext context, - @Cached PRaiseNativeNode.Lazy raiseNative) { - if (!(var instanceof PContextVar)) { - return raiseNative.get(inliningTarget).raise(null, marker, PythonBuiltinClassType.TypeError, ErrorMessages.INSTANCE_OF_CONTEXTVAR_EXPECTED); - } - PythonContext.PythonThreadState threadState = context.getThreadState(context.getLanguage(inliningTarget)); - Object result = ((PContextVar) var).getValue(inliningTarget, threadState); - if (result == null) { - if (def == PNone.NO_VALUE) { - if (((PContextVar) var).getDefault() == PContextVar.NO_DEFAULT) { - result = PNone.NO_VALUE; - } else { - result = ((PContextVar) var).getDefault(); - } - } else { - result = def; - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer, Pointer}, call = Ignored) + static long GraalPyPrivate_ContextVar_Get(long varPtr, long defPtr, long marker) { + Object var = NativeToPythonNode.executeRawUncached(varPtr); + if (!(var instanceof PContextVar pvar)) { + return PRaiseNativeNode.raiseStatic(marker, PythonBuiltinClassType.TypeError, ErrorMessages.INSTANCE_OF_CONTEXTVAR_EXPECTED); + } + PythonContext context = PythonContext.get(null); + PythonContext.PythonThreadState threadState = context.getThreadState(PythonLanguage.get(null)); + Object result = pvar.getValue(null, threadState); + if (result == null) { + if (defPtr == NULLPTR) { + result = pvar.getDefault() == PContextVar.NO_DEFAULT ? PNone.NO_VALUE : pvar.getDefault(); + } else { + result = NativeToPythonNode.executeRawUncached(defPtr); } - return result; } + return PythonToNativeNewRefNode.executeLongUncached(result); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct) - abstract static class PyContextVar_Set extends CApiBinaryBuiltinNode { - @Specialization - static Object doGeneric(Object var, Object val, - @Bind Node inliningTarget, - @Bind PythonContext pythonContext, - @Cached PRaiseNode raiseNode) { - if (!(var instanceof PContextVar pvar)) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.INSTANCE_OF_CONTEXTVAR_EXPECTED); - } - PythonLanguage language = pythonContext.getLanguage(inliningTarget); - PythonContext.PythonThreadState threadState = pythonContext.getThreadState(language); - Object oldValue = pvar.getValue(inliningTarget, threadState); - pvar.setValue(inliningTarget, threadState, val); - return PFactory.createContextVarsToken(language, pvar, oldValue); + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct) + static long PyContextVar_Set(long varPtr, long valPtr) { + Object var = NativeToPythonNode.executeRawUncached(varPtr); + Object val = NativeToPythonNode.executeRawUncached(valPtr); + if (!(var instanceof PContextVar pvar)) { + throw PRaiseNode.raiseStatic(null, PythonBuiltinClassType.TypeError, ErrorMessages.INSTANCE_OF_CONTEXTVAR_EXPECTED); } + PythonLanguage language = PythonLanguage.get(null); + PythonContext pythonContext = PythonContext.get(null); + PythonContext.PythonThreadState threadState = pythonContext.getThreadState(language); + Object oldValue = pvar.getValue(null, threadState); + pvar.setValue(null, threadState, val); + return PythonToNativeNewRefNode.executeLongUncached(PFactory.createContextVarsToken(language, pvar, oldValue)); } - @CApiBuiltin(ret = PyObjectTransfer, call = Direct) - abstract static class PyContext_CopyCurrent extends CApiNullaryBuiltinNode { - @Specialization - static Object doGeneric( - @Bind Node inliningTarget, - @Cached PyContextCopyCurrent copyCurrent) { - return copyCurrent.execute(inliningTarget); - } + @CApiBuiltin(ret = PyObjectRawPointer, call = Direct) + static long PyContext_CopyCurrent() { + return PythonToNativeNewRefNode.executeLongUncached(PyContextCopyCurrent.executeUncached()); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct) - abstract static class PyContext_Copy extends CApiUnaryBuiltinNode { - @Specialization - static Object doGeneric(PContextVarsContext context, - @Bind PythonLanguage language) { - return PFactory.copyContextVarsContext(language, context); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct) + static long PyContext_Copy(long contextPtr) { + PContextVarsContext context = (PContextVarsContext) NativeToPythonNode.executeRawUncached(contextPtr); + return PythonToNativeNewRefNode.executeLongUncached(PFactory.copyContextVarsContext(PythonLanguage.get(null), context)); } - @CApiBuiltin(ret = PyObjectTransfer, call = Direct) - abstract static class PyContext_New extends CApiNullaryBuiltinNode { - @Specialization - static Object doGeneric( - @Bind PythonLanguage language) { - return PFactory.createContextVarsContext(language); - } + @CApiBuiltin(ret = PyObjectRawPointer, call = Direct) + static long PyContext_New() { + return PythonToNativeNewRefNode.executeLongUncached(PFactory.createContextVarsContext(PythonLanguage.get(null))); } - @CApiBuiltin(ret = Int, args = {PyObject}, call = Direct) - abstract static class PyContext_Enter extends CApiUnaryBuiltinNode { - @Specialization - static Object doGeneric(PContextVarsContext context, - @Bind Node inliningTarget, - @Bind PythonContext pythonContext, - @Cached PRaiseNode raiseNode) { - PythonContext.PythonThreadState threadState = pythonContext.getThreadState(pythonContext.getLanguage(inliningTarget)); - context.enter(inliningTarget, threadState, raiseNode); - return 0; - } + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer}, call = Direct) + static int PyContext_Enter(long contextPtr) { + PContextVarsContext context = (PContextVarsContext) NativeToPythonNode.executeRawUncached(contextPtr); + PythonContext pythonContext = PythonContext.get(null); + PythonContext.PythonThreadState threadState = pythonContext.getThreadState(PythonLanguage.get(null)); + context.enter(null, threadState, PRaiseNode.getUncached()); + return 0; } - @CApiBuiltin(ret = Int, args = {PyObject}, call = Direct) - abstract static class PyContext_Exit extends CApiUnaryBuiltinNode { - @Specialization - static Object doGeneric(PContextVarsContext context, - @Bind Node inliningTarget, - @Bind PythonContext pythonContext) { - PythonContext.PythonThreadState threadState = pythonContext.getThreadState(pythonContext.getLanguage(inliningTarget)); - context.leave(threadState); - return 0; - } + @CApiBuiltin(ret = Int, args = {PyObjectRawPointer}, call = Direct) + static int PyContext_Exit(long contextPtr) { + PContextVarsContext context = (PContextVarsContext) NativeToPythonNode.executeRawUncached(contextPtr); + PythonContext pythonContext = PythonContext.get(null); + PythonContext.PythonThreadState threadState = pythonContext.getThreadState(PythonLanguage.get(null)); + context.leave(threadState); + return 0; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextDescrBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextDescrBuiltins.java index c78496a1bc..f6b55a4598 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextDescrBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextDescrBuiltins.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 @@ -48,22 +48,29 @@ 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.common.CExtContext.isClassOrStaticMethod; +import static com.oracle.graal.python.nodes.HiddenAttr.METHOD_DEF_PTR; +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; +import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi6BuiltinNode; -import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi7BuiltinNode; 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.modules.cext.PythonCextTypeBuiltins.CreateGetSetNode; -import com.oracle.graal.python.builtins.modules.cext.PythonCextTypeBuiltins.NewClassMethodNode; +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.cext.capi.MethodDescriptorWrapper; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonClassInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode; +import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.mappingproxy.MappingproxyBuiltins; +import com.oracle.graal.python.builtins.objects.method.PDecoratedMethod; +import com.oracle.graal.python.nodes.HiddenAttr; +import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.CompilerAsserts; 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 PythonCextDescrBuiltins { @@ -81,30 +88,26 @@ static Object values(Object obj, abstract static class GraalPyPrivate_Descr_NewGetSet extends CApi6BuiltinNode { @Specialization - static Object doNativeCallable(TruffleString name, Object cls, Object getter, Object setter, Object doc, Object closure, - @Bind Node inliningTarget, - @Cached CreateGetSetNode createGetSetNode) { - return createGetSetNode.execute(inliningTarget, name, cls, getter, setter, doc, closure); + static Object doNativeCallable(TruffleString name, Object cls, long getter, long setter, Object doc, long closure) { + return PythonCextTypeBuiltins.createGetSet(name, cls, getter, setter, doc, closure); } } - @CApiBuiltin(ret = PyObjectTransfer, args = {Pointer, ConstCharPtrAsTruffleString, ConstCharPtrAsTruffleString, Int, Int, Pointer, PyTypeObject}, call = Ignored) - abstract static class GraalPyPrivate_Descr_NewClassMethod extends CApi7BuiltinNode { - - @Specialization - static Object doNativeCallable(Object methodDefPtr, TruffleString name, Object doc, int flags, Object wrapper, Object methObj, Object type, - @Bind Node inliningTarget, - @Cached NewClassMethodNode newClassMethodNode, - @Bind PythonLanguage language) { - Object func = newClassMethodNode.execute(inliningTarget, methodDefPtr, name, methObj, flags, wrapper, type, doc); - if (!isClassOrStaticMethod(flags)) { - /* - * NewClassMethodNode only wraps method with METH_CLASS and METH_STATIC set but we - * need to do so here. - */ - func = PFactory.createClassmethodFromCallableObj(language, func); - } - return func; - } + /** Implementation of {@code PyDescr_NewClassMethod}. */ + @CApiBuiltin(ret = PyObjectTransfer, args = {Pointer, ConstCharPtrAsTruffleString, ConstCharPtrAsTruffleString, Int, Pointer, PyTypeObject}, call = Ignored) + public static long GraalPyPrivate_Descr_NewClassMethod(long methodDefPtr, long nameRaw, long docRaw, int flags, long methPtr, long typeRaw) { + CompilerAsserts.neverPartOfCompilation(); + PythonLanguage language = PythonLanguage.get(null); + TruffleString name = (TruffleString) CharPtrToPythonNode.getUncached().execute(nameRaw); + Object doc = CharPtrToPythonNode.getUncached().execute(docRaw); + assert doc == PNone.NO_VALUE || doc instanceof TruffleString; + Object type = NativeToPythonClassInternalNode.executeUncached(typeRaw); + PBuiltinFunction func = MethodDescriptorWrapper.createWrapperFunction(language, name, methPtr, type, flags); + assert func != null; + PDecoratedMethod classMethod = PFactory.createClassmethodFromCallableObj(language, func); + WriteAttributeToPythonObjectNode.executeUncached(classMethod, T___NAME__, name); + WriteAttributeToPythonObjectNode.executeUncached(classMethod, T___DOC__, doc); + HiddenAttr.WriteLongNode.executeUncached(classMethod, METHOD_DEF_PTR, methodDefPtr); + return PythonToNativeInternalNode.executeUncached(classMethod, true); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextDictBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextDictBuiltins.java index 003ddec5ae..ae2ce9e9f2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextDictBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextDictBuiltins.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 @@ -47,6 +47,7 @@ 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_HASH_T_PTR; 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.PyObjectBorrowed; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectPtr; @@ -54,11 +55,15 @@ import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_hash_t; 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.Void; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readLong; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.writeLong; import static com.oracle.graal.python.nodes.ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC_WAS_S_P; import static com.oracle.graal.python.nodes.ErrorMessages.HASH_MISMATCH; import static com.oracle.graal.python.nodes.ErrorMessages.OBJ_P_HAS_NO_ATTR_S; import static com.oracle.graal.python.nodes.SpecialMethodNames.T_KEYS; import static com.oracle.graal.python.nodes.SpecialMethodNames.T_UPDATE; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import java.util.logging.Level; @@ -75,7 +80,8 @@ 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.transitions.CApiTransitions; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.GcNativePtrToPythonNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.HandlePointerConverter; import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage; import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage; import com.oracle.graal.python.builtins.objects.common.HashingCollectionNodes.SetItemNode; @@ -108,6 +114,7 @@ import com.oracle.graal.python.lib.PyObjectGetAttr; import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; import com.oracle.graal.python.nodes.call.CallNode; @@ -116,6 +123,7 @@ 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.TruffleLogger; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; @@ -126,14 +134,13 @@ 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; import com.oracle.truffle.api.profiles.InlinedLoopConditionProfile; public final class PythonCextDictBuiltins { + private static final TruffleLogger LOGGER = CApiContext.getLogger(PythonCextDictBuiltins.class); @CApiBuiltin(ret = PyObjectTransfer, args = {}, call = Direct) abstract static class PyDict_New extends CApiNullaryBuiltinNode { @@ -149,12 +156,8 @@ static Object run( abstract static class _PyDict_Next extends CApi5BuiltinNode { @Specialization - static int next(PDict dict, Object posPtr, Object keyPtr, Object valuePtr, Object hashPtr, + static int next(PDict dict, long posPtr, long keyPtr, long valuePtr, long hashPtr, @Bind Node inliningTarget, - @CachedLibrary(limit = "2") InteropLibrary lib, - @Cached CStructAccess.ReadI64Node readI64Node, - @Cached CStructAccess.WriteLongNode writeLongNode, - @Cached CStructAccess.WritePointerNode writePointerNode, @Cached CApiTransitions.PythonToNativeNode toNativeNode, @Cached InlinedBranchProfile needsRewriteProfile, @Cached InlinedBranchProfile economicMapProfile, @@ -173,7 +176,7 @@ static int next(PDict dict, Object posPtr, Object keyPtr, Object valuePtr, Objec * whole dict at once in the first call (which is required to start with position 0). In * order to not violate the ordering, we construct a completely new storage. */ - long pos = readI64Node.read(posPtr); + long pos = readLong(posPtr); if (pos == 0) { HashingStorage storage = dict.getDictStorage(); int len = lenNode.execute(inliningTarget, storage); @@ -231,22 +234,22 @@ static int next(PDict dict, Object posPtr, Object keyPtr, Object valuePtr, Objec return 0; } long newPos = it.getState() + 1; - writeLongNode.write(posPtr, newPos); - if (!lib.isNull(keyPtr)) { + writeLong(posPtr, newPos); + if (keyPtr != NULLPTR) { Object key = itKey.execute(inliningTarget, storage, it); assert promoteKeyNode.execute(inliningTarget, key) == null; // Borrowed reference - writePointerNode.write(keyPtr, toNativeNode.execute(key)); + NativeMemory.writePtr(keyPtr, toNativeNode.executeLong(key)); } - if (!lib.isNull(valuePtr)) { + if (valuePtr != NULLPTR) { Object value = itValue.execute(inliningTarget, storage, it); assert promoteValueNode.execute(inliningTarget, value) == null; // Borrowed reference - writePointerNode.write(valuePtr, toNativeNode.execute(value)); + NativeMemory.writePtr(valuePtr, toNativeNode.executeLong(value)); } - if (!lib.isNull(hashPtr)) { + if (hashPtr != NULLPTR) { long hash = itKeyHash.execute(null, inliningTarget, storage, it); - writeLongNode.write(hashPtr, hash); + NativeMemory.writeLong(hashPtr, hash); } return 1; } @@ -275,14 +278,14 @@ public Object fallback(Object dict, @SuppressWarnings("unused") Object key, @Sup @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Direct) abstract static class PyDict_Size extends CApiUnaryBuiltinNode { @Specialization - static int size(PDict dict, + static long size(PDict dict, @Bind Node inliningTarget, @Cached HashingStorageLen lenNode) { return lenNode.execute(inliningTarget, dict.getDictStorage()); } @Fallback - public int fallback(Object dict) { + public long fallback(Object dict) { throw raiseFallback(dict, PythonBuiltinClassType.PDict); } } @@ -317,7 +320,7 @@ static Object getItem(PDict dict, Object key, Object res = getItem.execute(null, inliningTarget, dict.getDictStorage(), key); if (res == null) { noResultProfile.enter(inliningTarget); - return getNativeNull(inliningTarget); + return NATIVE_NULL; } Object promotedValue = promoteNode.execute(inliningTarget, res); if (promotedValue != null) { @@ -327,7 +330,7 @@ static Object getItem(PDict dict, Object key, return res; } catch (PException e) { // PyDict_GetItem suppresses all exceptions for historical reasons - return getNativeNull(inliningTarget); + return NATIVE_NULL; } } @@ -355,7 +358,7 @@ static Object getItem(PDict dict, Object key, Object res = getItem.execute(null, inliningTarget, dict.getDictStorage(), key); if (res == null) { noResultProfile.enter(inliningTarget); - return getNativeNull(inliningTarget); + return NATIVE_NULL; } Object promotedValue = promoteNode.execute(inliningTarget, res); if (promotedValue != null) { @@ -647,12 +650,12 @@ static Boolean doGeneric(@SuppressWarnings("unused") VirtualFrame frame, Node in } Object key = nextKey.execute(inliningTarget, storage, it); - if (isTracked(key, null)) { + if (isTracked(key)) { return false; } Object value = nextValue.execute(inliningTarget, storage, it); - if (isTracked(value, null)) { + if (isTracked(value)) { return false; } return true; @@ -662,7 +665,7 @@ static Boolean doGeneric(@SuppressWarnings("unused") VirtualFrame frame, Node in * #define _PyObject_GC_MAY_BE_TRACKED(obj) \ (PyObject_IS_GC(obj) && \ * (!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj))) */ - static boolean isTracked(Object object, CStructAccess.ReadI64Node readI64Node) { + static boolean isTracked(Object object) { // TODO(fa): implement properly return true; // #define _PyObject_GC_IS_TRACKED(o) (_PyGCHead_UNTAG(_Py_AS_GC(o))->_gc_next != 0) @@ -702,4 +705,35 @@ static int check(PDict dict, return 1; } } + + @CApiBuiltin(ret = Void, args = {Pointer}, call = Ignored) + abstract static class GraalPyPrivate_Dict_UnlinkNativePart extends CApiUnaryBuiltinNode { + + @Specialization + static Object doLong(long pointer, + @Bind Node inliningTarget, + @Cached GcNativePtrToPythonNode nativeToPythonNode) { + assert !HandlePointerConverter.pointsToPyHandleSpace(pointer); + Object resolved = nativeToPythonNode.execute(inliningTarget, pointer); + if (resolved == null) { + /* + * This is fine because the managed object was already collected and deallocation + * was triggered from managed side (e.g. context finalization). We don't need to do + * anything. + */ + return PNone.NO_VALUE; + } + if (!(resolved instanceof PDict self)) { + LOGGER.severe(PythonUtils.formatJString("Expected pointer 0x%x to be linked to a managed dict but was %s", pointer, resolved)); + return PNone.NO_VALUE; + } + assert self.isNative(); + assert self.getNativePointer() == pointer; + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer(PythonUtils.formatJString("Unlinking managed dict %s from native part 0x%x", self, self.getNativePointer())); + } + self.clearNativePointer(); + return PNone.NO_VALUE; + } + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java index c7835533fe..9e62747c76 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java @@ -61,6 +61,7 @@ import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___TRACEBACK__; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import com.oracle.graal.python.PythonLanguage; @@ -127,8 +128,8 @@ import com.oracle.truffle.api.strings.TruffleString; public final class PythonCextErrBuiltins { - private static Object noneToNativeNull(Node node, Object obj) { - return obj instanceof PNone ? PythonContext.get(node).getNativeNull() : obj; + private static Object noneToNativeNull(Object obj) { + return obj instanceof PNone ? NATIVE_NULL : obj; } @CApiBuiltin(ret = Void, args = {PyObject, PyObject}, call = Ignored) @@ -156,7 +157,7 @@ abstract static class _PyErr_SetHandledException extends CApiBinaryBuiltinNode { @Specialization @SuppressWarnings("unused") - static Object doClear(@SuppressWarnings("unused") Object threadState, PNone val, + static Object doClear(@SuppressWarnings("unused") long threadState, PNone val, @Bind Node inliningTarget, @Bind PythonContext context) { PythonLanguage lang = context.getLanguage(inliningTarget); @@ -165,7 +166,7 @@ static Object doClear(@SuppressWarnings("unused") Object threadState, PNone val, } @Specialization - static Object doFull(@SuppressWarnings("unused") Object threadState, PBaseException val, + static Object doFull(@SuppressWarnings("unused") long threadState, PBaseException val, @Bind Node inliningTarget, @Bind PythonContext context) { PythonLanguage language = context.getLanguage(inliningTarget); @@ -297,11 +298,11 @@ Object info( AbstractTruffleException currentException = getCaughtExceptionNode.executeFromNative(); if (currentException == null) { noExceptionProfile.enter(inliningTarget); - return getNativeNull(); + return NATIVE_NULL; } assert currentException != PException.NO_EXCEPTION; Object exception = getEscapedExceptionNode.execute(inliningTarget, currentException); - Object traceback = noneToNativeNull(inliningTarget, getTracebackNode.execute(inliningTarget, exception)); + Object traceback = noneToNativeNull(getTracebackNode.execute(inliningTarget, exception)); return PFactory.createTuple(language, new Object[]{getClassNode.execute(inliningTarget, exception), exception, traceback}); } } @@ -310,13 +311,13 @@ Object info( abstract static class _PyErr_GetHandledException extends CApiUnaryBuiltinNode { @Specialization - static Object get(@SuppressWarnings("unused") Object threadState, + static Object get(@SuppressWarnings("unused") long threadState, @Bind Node inliningTarget, @Cached GetCaughtExceptionNode getCaughtExceptionNode, @Cached GetEscapedExceptionNode getEscapedExceptionNode) { AbstractTruffleException caughtException = getCaughtExceptionNode.executeFromNative(); if (caughtException == null) { - return PythonContext.get(inliningTarget).getNativeNull(); + return NATIVE_NULL; } assert caughtException != PException.NO_EXCEPTION; return getEscapedExceptionNode.execute(inliningTarget, caughtException); @@ -454,7 +455,7 @@ abstract static class PyException_GetCause extends CApiUnaryBuiltinNode { Object getCause(Object exc, @Bind Node inliningTarget, @Cached ExceptionNodes.GetCauseNode getCauseNode) { - return noneToNativeNull(inliningTarget, getCauseNode.execute(inliningTarget, exc)); + return noneToNativeNull(getCauseNode.execute(inliningTarget, exc)); } } @@ -464,7 +465,7 @@ abstract static class PyException_GetContext extends CApiUnaryBuiltinNode { Object setCause(Object exc, @Bind Node inliningTarget, @Cached ExceptionNodes.GetContextNode getContextNode) { - return noneToNativeNull(inliningTarget, getContextNode.execute(inliningTarget, exc)); + return noneToNativeNull(getContextNode.execute(inliningTarget, exc)); } } @@ -486,7 +487,7 @@ abstract static class PyException_GetTraceback extends CApiUnaryBuiltinNode { Object getTraceback(Object exc, @Bind Node inliningTarget, @Cached ExceptionNodes.GetTracebackNode getTracebackNode) { - return noneToNativeNull(inliningTarget, getTracebackNode.execute(inliningTarget, exc)); + return noneToNativeNull(getTracebackNode.execute(inliningTarget, exc)); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFloatBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFloatBuiltins.java index b883813bde..a078f1a38c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFloatBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFloatBuiltins.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 @@ -67,18 +67,7 @@ static double fromDouble(double d) { @CApiBuiltin(ret = ArgDescriptor.Double, args = {PyObject}, call = Ignored) abstract static class GraalPyPrivate_Float_AsDouble extends CApiUnaryBuiltinNode { - - @Specialization - static double doLongNativeWrapper(long object) { - return object; - } - @Specialization - static double doDoubleNativeWrapper(double object) { - return object; - } - - @Specialization(guards = {"!isLong(object)", "!isDouble(object)"}) static double doGenericErr(Object object, @Bind Node inliningTarget, @Cached PyFloatAsDoubleNode asDoubleNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFrameBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFrameBuiltins.java index f1fc77edfe..a494d0790a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFrameBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextFrameBuiltins.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 @@ -46,6 +46,7 @@ import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyFrameObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyFrameObjectTransfer; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin; @@ -114,7 +115,7 @@ Object get(PFrame frame, @Cached FrameBuiltins.GetBackrefNode getBackNode) { Object back = getBackNode.execute(null, frame); if (back == PNone.NONE) { - return getNativeNull(); + return NATIVE_NULL; } return back; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextHashBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextHashBuiltins.java index 9e90e63f7c..6e22401390 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextHashBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextHashBuiltins.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 @@ -56,8 +56,8 @@ 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.cext.capi.transitions.ArgDescriptor; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.lib.PyObjectHashNode; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; @@ -71,9 +71,9 @@ public final class PythonCextHashBuiltins { abstract static class GraalPyPrivate_Hash_InitSecret extends CApiUnaryBuiltinNode { @Specialization @TruffleBoundary - Object get(Object secretPtr, - @Cached CStructAccess.WriteByteNode writeNode) { - writeNode.writeByteArray(secretPtr, getContext().getHashSecret()); + Object get(long secretPtr) { + byte[] hashSecret = getContext().getHashSecret(); + NativeMemory.writeByteArrayElements(secretPtr, 0L, hashSecret, 0, hashSecret.length); return PNone.NO_VALUE; } } @@ -113,11 +113,10 @@ abstract static class _Py_HashBytes extends CApiBinaryBuiltinNode { @Specialization @TruffleBoundary - static long doI(Object value, long size, - @Cached CStructAccess.ReadByteNode readNode, + static long doI(long value, long size, @Cached TruffleString.FromByteArrayNode toString, @Cached HashCodeNode hashNode) { - byte[] array = readNode.readByteArray(value, (int) size); + byte[] array = NativeMemory.readByteArrayElements(value, 0, (int) size); TruffleString string = toString.execute(array, TS_ENCODING_BINARY, false); return PyObjectHashNode.hash(string, hashNode); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextImportBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextImportBuiltins.java index 49c66cb09a..0276b38d07 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextImportBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextImportBuiltins.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 @@ -54,6 +54,7 @@ import static com.oracle.graal.python.nodes.BuiltinNames.T___IMPORT__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___INITIALIZING__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___SPEC__; +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.PythonCextBuiltins.CApi5BuiltinNode; @@ -132,7 +133,7 @@ Object getModule(Object name, try { m = getItem.execute(null, inliningTarget, modules, name); } catch (PException e) { - return context.getNativeNull(); + return NATIVE_NULL; } if (m != PNone.NONE) { boolean initializing = false; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextListBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextListBuiltins.java index 3536cee5ec..039c33c3df 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextListBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextListBuiltins.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 static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectBorrowed; 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.runtime.nativeaccess.NativeMemory.writePtr; import static com.oracle.graal.python.nodes.ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC_S; import java.util.Arrays; @@ -68,7 +69,6 @@ import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.XDecRefPointerNode; import com.oracle.graal.python.builtins.objects.cext.capi.PySequenceArrayWrapper; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetItemScalarNode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ListGeneralizationNode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.SetItemScalarNode; @@ -243,12 +243,12 @@ Object fallback(Object list, @SuppressWarnings("unused") Object iterable) { abstract static class PyList_Size extends CApiUnaryBuiltinNode { @Specialization - static int size(PList list) { + static long size(PList list) { return list.getSequenceStorage().length(); } @Fallback - int fallback(Object list) { + long fallback(Object list) { throw raiseFallback(list, PythonBuiltinClassType.PList); } } @@ -305,13 +305,12 @@ static int error(@SuppressWarnings("unused") Object self, abstract static class GraalPyPrivate_List_ClearManagedOrGetItems extends CApiBinaryBuiltinNode { @Specialization - static long doGeneric(PList self, Object outItems, + static long doGeneric(PList self, long outItems, @Bind Node inliningTarget, - @Cached CStructAccess.WritePointerNode writePointerNode, @Cached XDecRefPointerNode xDecRefPointerNode) { SequenceStorage sequenceStorage = self.getSequenceStorage(); if (sequenceStorage instanceof NativeObjectSequenceStorage nativeStorage) { - writePointerNode.write(outItems, nativeStorage.getPtr()); + writePtr(outItems, nativeStorage.getPtr()); int length = nativeStorage.length(); nativeStorage.setNewLength(0); return length; @@ -336,9 +335,8 @@ static long doGeneric(PList self, Object outItems, abstract static class GraalPyPrivate_List_TryGetItems extends CApiBinaryBuiltinNode { @Specialization - static long doGeneric(PList self, Object outItems, + static long doGeneric(PList self, long outItems, @Bind Node inliningTarget, - @Cached CStructAccess.WritePointerNode writePointerNode, @Cached PySequenceArrayWrapper.ToNativeStorageNode toNativeStorageNode) { SequenceStorage sequenceStorage = self.getSequenceStorage(); if (sequenceStorage instanceof ObjectSequenceStorage objectStorage) { @@ -346,7 +344,7 @@ static long doGeneric(PList self, Object outItems, self.setSequenceStorage(sequenceStorage); } if (sequenceStorage instanceof NativeObjectSequenceStorage nativeStorage) { - writePointerNode.write(outItems, nativeStorage.getPtr()); + writePtr(outItems, nativeStorage.getPtr()); return nativeStorage.length(); } return 0; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextLongBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextLongBuiltins.java index c92dc168a4..5f6a73ecc1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextLongBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextLongBuiltins.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 @@ -57,6 +57,7 @@ import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UNSIGNED_CHAR_PTR; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UNSIGNED_LONG; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.UNSIGNED_LONG_LONG; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readByteArrayElements; import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError; import java.math.BigInteger; @@ -69,19 +70,18 @@ import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiQuaternaryBuiltinNode; 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.objects.cext.PythonNativeVoidPtr; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.CastToNativeLongNode; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor; import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.ConvertPIntToPrimitiveNode; -import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.ConvertPIntToPrimitiveNodeGen; import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.TransformPExceptionToNativeCachedNode; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; +import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodesFactory.ConvertPIntToPrimitiveNodeGen; import com.oracle.graal.python.builtins.objects.ints.IntBuiltins; import com.oracle.graal.python.builtins.objects.ints.IntNodes; import com.oracle.graal.python.builtins.objects.ints.PInt; import com.oracle.graal.python.lib.PyLongFromDoubleNode; import com.oracle.graal.python.lib.PyLongFromUnicodeObject; +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.classes.IsSubtypeNode; @@ -98,9 +98,6 @@ import com.oracle.truffle.api.dsl.Cached.Shared; 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.UnsupportedMessageException; -import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.UnexpectedResultException; import com.oracle.truffle.api.profiles.InlinedBranchProfile; @@ -293,20 +290,6 @@ abstract static class GraalPyPrivate_Long_FromLongLong extends CApiUnaryBuiltinN static long doSignedLong(long n) { return n; } - - @Specialization(guards = "!isInteger(pointer)", limit = "2") - static Object doPointer(Object pointer, - @CachedLibrary("pointer") InteropLibrary lib) { - // We capture the native pointer at the time when we create the wrapper if it exists. - if (lib.isPointer(pointer)) { - try { - return PFactory.createNativeVoidPtr(pointer, lib.asPointer(pointer)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - return PFactory.createNativeVoidPtr(pointer); - } } @CApiBuiltin(name = "PyLong_FromSize_t", ret = PyObjectTransfer, args = {SIZE_T}, call = Direct) @@ -314,14 +297,6 @@ static Object doPointer(Object pointer, @CApiBuiltin(ret = PyObjectTransfer, args = {UNSIGNED_LONG_LONG}, call = Direct) abstract static class PyLong_FromUnsignedLongLong extends CApiUnaryBuiltinNode { - @Specialization - static long doUnsignedInt(int n) { - if (n < 0) { - return n & 0xFFFFFFFFL; - } - return n; - } - @Specialization(guards = "n >= 0") static Object doUnsignedLongPositive(long n) { return n; @@ -333,20 +308,6 @@ static Object doUnsignedLongNegative(long n, return PFactory.createInt(language, convertToBigInteger(n)); } - @Specialization(guards = "!isInteger(pointer)", limit = "2") - static Object doPointer(Object pointer, - @CachedLibrary("pointer") InteropLibrary lib) { - // We capture the native pointer at the time when we create the wrapper if it exists. - if (lib.isPointer(pointer)) { - try { - return PFactory.createNativeVoidPtr(pointer, lib.asPointer(pointer)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - return PFactory.createNativeVoidPtr(pointer); - } - @TruffleBoundary private static BigInteger convertToBigInteger(long n) { return BigInteger.valueOf(n).add(BigInteger.ONE.shiftLeft(Long.SIZE)); @@ -386,11 +347,6 @@ long doPointer(PInt n, } } - @Specialization - static Object doPointer(PythonNativeVoidPtr n) { - return n.getPointerObject(); - } - @Fallback long doGeneric(Object n, @Bind Node inliningTarget, @@ -431,38 +387,35 @@ private static void checkSign(Node inliningTarget, boolean negative, int isSigne } @Specialization - static Object get(int value, Object bytes, long n, int littleEndian, int isSigned, + static Object get(int value, long bytes, long n, int littleEndian, int isSigned, @Bind Node inliningTarget, @Shared @Cached InlinedConditionProfile profile, - @Shared @Cached CStructAccess.WriteByteNode write, @Shared @Cached PRaiseNode raiseNode) { checkSign(inliningTarget, value < 0, isSigned, raiseNode); byte[] array = IntBuiltins.ToBytesNode.fromLong(value, PythonUtils.toIntError(n), littleEndian == 0, isSigned != 0, inliningTarget, profile, raiseNode); - write.writeByteArray(bytes, array); + NativeMemory.writeByteArrayElements(bytes, 0L, array, 0, array.length); return 0; } @Specialization - static Object get(long value, Object bytes, long n, int littleEndian, int isSigned, + static Object get(long value, long bytes, long n, int littleEndian, int isSigned, @Bind Node inliningTarget, @Shared @Cached InlinedConditionProfile profile, - @Shared @Cached CStructAccess.WriteByteNode write, @Shared @Cached PRaiseNode raiseNode) { checkSign(inliningTarget, value < 0, isSigned, raiseNode); byte[] array = IntBuiltins.ToBytesNode.fromLong(value, PythonUtils.toIntError(n), littleEndian == 0, isSigned != 0, inliningTarget, profile, raiseNode); - write.writeByteArray(bytes, array); + NativeMemory.writeByteArrayElements(bytes, 0L, array, 0, array.length); return 0; } @Specialization - static Object get(PInt value, Object bytes, long n, int littleEndian, int isSigned, + static Object get(PInt value, long bytes, long n, int littleEndian, int isSigned, @Bind Node inliningTarget, @Shared @Cached InlinedConditionProfile profile, - @Shared @Cached CStructAccess.WriteByteNode write, @Shared @Cached PRaiseNode raiseNode) { checkSign(inliningTarget, value.isNegative(), isSigned, raiseNode); byte[] array = IntBuiltins.ToBytesNode.fromBigInteger(value, PythonUtils.toIntError(n), littleEndian == 0, isSigned != 0, inliningTarget, profile, raiseNode); - write.writeByteArray(bytes, array); + NativeMemory.writeByteArrayElements(bytes, 0L, array, 0, array.length); return 0; } } @@ -480,15 +433,14 @@ static Object convert(Object s, int base, @CApiBuiltin(ret = PyObjectTransfer, args = {CONST_UNSIGNED_CHAR_PTR, SIZE_T, Int, Int}, call = Direct) abstract static class _PyLong_FromByteArray extends CApiQuaternaryBuiltinNode { @Specialization - static Object convert(Object charPtr, long size, int littleEndian, int signed, + static Object convert(long charPtr, long size, int littleEndian, int signed, @Bind Node inliningTarget, - @Cached CStructAccess.ReadByteNode readByteNode, @Cached IntNodes.PyLongFromByteArray fromByteArray, @Cached PRaiseNode raiseNode) { if (size != (int) size) { throw raiseNode.raise(inliningTarget, OverflowError, ErrorMessages.BYTE_ARRAY_TOO_LONG_TO_CONVERT_TO_INT); } - byte[] bytes = readByteNode.readByteArray(charPtr, (int) size); + byte[] bytes = readByteArrayElements(charPtr, 0, (int) size); return fromByteArray.execute(inliningTarget, bytes, littleEndian != 0, signed != 0); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextMethodBuiltins.java index 89e39f7d5b..abe1de2c99 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextMethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextMethodBuiltins.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 @@ -48,26 +48,26 @@ 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.common.CExtContext.METH_METHOD; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__; import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi9BuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes; +import com.oracle.graal.python.builtins.objects.cext.capi.MethodDescriptorWrapper; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode; +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.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod; +import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; +import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.truffle.api.dsl.Bind; -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.Specialization; -import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; public final class PythonCextMethodBuiltins { @@ -79,46 +79,41 @@ public final class PythonCextMethodBuiltins { */ public static final HiddenAttr METHOD_DEF_PTR = HiddenAttr.METHOD_DEF_PTR; - @GenerateInline - @GenerateCached(false) - abstract static class CFunctionNewExMethodNode extends Node { + static PythonBuiltinObject cFunctionNewExMethodNode(PythonLanguage language, long methodDefPtr, TruffleString name, long methPtr, int flags, Object self, Object moduleName, Object cls, + Object doc) { + PBuiltinFunction func = MethodDescriptorWrapper.createWrapperFunction(language, name, methPtr, PNone.NO_VALUE, flags); + HiddenAttr.WriteLongNode.executeUncached(func, METHOD_DEF_PTR, methodDefPtr); + WriteAttributeToPythonObjectNode.executeUncached(func, T___NAME__, name); + WriteAttributeToPythonObjectNode.executeUncached(func, T___DOC__, doc); + PBuiltinMethod method; + if (cls != PNone.NO_VALUE) { + method = PFactory.createBuiltinMethod(language, self, func, cls); + } else { + method = PFactory.createBuiltinMethod(language, self, func); + } + assert moduleName == PNone.NO_VALUE || PyUnicodeCheckNode.executeUncached(moduleName); + WriteAttributeToPythonObjectNode.executeUncached(method, T___MODULE__, moduleName); + return method; + } - abstract Object execute(Node inliningTarget, Object methodDefPtr, TruffleString name, Object methObj, int flags, int wrapper, Object self, Object module, Object cls, Object doc); + @CApiBuiltin(ret = PyObjectTransfer, args = {PyMethodDef, ConstCharPtrAsTruffleString, Pointer, Int, PyObject, PyObject, PyTypeObject, ConstCharPtrAsTruffleString}, call = Ignored) + public static long GraalPyPrivate_CMethod_NewEx(long methodDefPtr, long nameRaw, long methPtr, int flags, long selfRaw, long moduleRaw, long clsRaw, long docRaw) { + // errors are expected to be thrown already in native code + assert verifyFlags(flags, clsRaw); - final Object execute(Node inliningTarget, Object methodDefPtr, TruffleString name, Object methObj, int flags, int wrapper, Object self, Object module, Object doc) { - return execute(inliningTarget, methodDefPtr, name, methObj, flags, wrapper, self, module, PNone.NO_VALUE, doc); - } + TruffleString name = (TruffleString) CharPtrToPythonNode.getUncached().execute(nameRaw); + Object self = NativeToPythonInternalNode.executeUncached(selfRaw, false); + Object module = NativeToPythonInternalNode.executeUncached(moduleRaw, false); + Object cls = NativeToPythonInternalNode.executeUncached(clsRaw, false); + Object doc = CharPtrToPythonNode.getUncached().execute(docRaw); + assert doc == PNone.NO_VALUE || doc instanceof TruffleString; - @Specialization - static Object doNativeCallable(Node inliningTarget, Object methodDefPtr, TruffleString name, Object methObj, int flags, int wrapper, Object self, Object module, Object cls, Object doc, - @Bind PythonLanguage language, - @Cached HiddenAttr.WriteNode writeHiddenAttrNode, - @Cached(inline = false) WriteAttributeToPythonObjectNode writeAttrNode) { - Object f = ExternalFunctionNodes.PExternalFunctionWrapper.createWrapperFunction(name, methObj, PNone.NO_VALUE, flags, wrapper, language); - assert f instanceof PBuiltinFunction; - PBuiltinFunction func = (PBuiltinFunction) f; - writeHiddenAttrNode.execute(inliningTarget, func, METHOD_DEF_PTR, methodDefPtr); - writeAttrNode.execute(func, T___NAME__, name); - writeAttrNode.execute(func, T___DOC__, doc); - PBuiltinMethod method; - if (cls != PNone.NO_VALUE) { - method = PFactory.createBuiltinMethod(language, self, func, cls); - } else { - method = PFactory.createBuiltinMethod(language, self, func); - } - writeAttrNode.execute(method, T___MODULE__, module); - return method; - } + PythonBuiltinObject result = cFunctionNewExMethodNode(PythonLanguage.get(null), methodDefPtr, name, methPtr, flags, self, module, cls, doc); + return PythonToNativeInternalNode.executeUncached(result, true); } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyMethodDef, ConstCharPtrAsTruffleString, Pointer, Int, Int, PyObject, PyObject, PyTypeObject, ConstCharPtrAsTruffleString}, call = Ignored) - abstract static class GraalPyPrivate_CMethod_NewEx extends CApi9BuiltinNode { - - @Specialization - static Object doNativeCallable(Object methodDefPtr, TruffleString name, Object methObj, int flags, int wrapper, Object self, Object module, Object cls, Object doc, - @Bind Node inliningTarget, - @Cached CFunctionNewExMethodNode cFunctionNewExMethodNode) { - return cFunctionNewExMethodNode.execute(inliningTarget, methodDefPtr, name, methObj, flags, wrapper, self, module, cls, doc); - } + private static boolean verifyFlags(int flags, long clsRaw) { + boolean isMethod = (flags & METH_METHOD) != 0; + return (!isMethod || clsRaw != NULLPTR) && (isMethod || clsRaw == NULLPTR); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java index 149cb08fb9..483c15a192 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java @@ -47,41 +47,51 @@ 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.Pointer; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyMethodDef; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyModuleDef; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyModuleObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyModuleObjectTransfer; 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.PyObjectAsTruffleString; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer; +import static com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.bindFunctionPointer; +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.runtime.nativeaccess.NativeMemory.NULLPTR; +import static com.oracle.graal.python.nodes.ErrorMessages.NAMELESS_MODULE; import static com.oracle.graal.python.nodes.ErrorMessages.S_NEEDS_S_AS_FIRST_ARG; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___FILE__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___PACKAGE__; +import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; -import static com.oracle.graal.python.util.PythonUtils.tsLiteral; +import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi7BuiltinNode; 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.PythonCextMethodBuiltins.CFunctionNewExMethodNode; import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.PRaiseNativeNodeGen; +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.ExternalFunctionNodes.ExternalFunctionInvokeNode; -import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper; +import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionSignature; 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.CharPtrToPythonNode; +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.EnsureExecutableNode; 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.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; +import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; import com.oracle.graal.python.builtins.objects.str.StringBuiltins.PrefixSuffixNode; import com.oracle.graal.python.lib.PyUnicodeCheckNode; +import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; @@ -91,9 +101,9 @@ import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.object.GetClassNode; +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.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -101,8 +111,6 @@ 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.InteropLibrary; -import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @@ -243,38 +251,63 @@ static Object pop(@SuppressWarnings("unused") Object m, @SuppressWarnings("unuse } } - @CApiBuiltin(ret = Int, args = {Pointer, PyObject, ConstCharPtrAsTruffleString, Pointer, Int, Int, ConstCharPtrAsTruffleString}, call = Ignored) - abstract static class GraalPyPrivate_Module_AddFunctionToModule extends CApi7BuiltinNode { + @CApiBuiltin(ret = Int, args = {PyObject, PyMethodDef}, call = Ignored) + public static int GraalPyPrivate_Module_AddFunctions(long moduleRaw, long functions) { + CompilerAsserts.neverPartOfCompilation(); + Object module = NativeToPythonInternalNode.executeUncached(moduleRaw, false); - @Specialization - static Object moduleFunction(Object methodDefPtr, PythonModule mod, TruffleString name, Object cfunc, int flags, int wrapper, Object doc, - @Bind Node inliningTarget, - @Cached ObjectBuiltins.SetattrNode setattrNode, - @Cached(inline = true) ReadAttributeFromPythonObjectNode readAttrNode, - @Cached CFunctionNewExMethodNode cFunctionNewExMethodNode) { - Object modName = readAttrNode.execute(inliningTarget, mod, T___NAME__, null); - assert modName != null : "module name is missing!"; - Object func = cFunctionNewExMethodNode.execute(inliningTarget, methodDefPtr, name, cfunc, flags, wrapper, mod, modName, doc); - setattrNode.executeSetAttr(null, mod, name, func); - return 0; + // the necessary type check is done in the C function + assert module instanceof PythonModule; + Object modName = ReadAttributeFromPythonObjectNode.executeUncached((PythonModule) module, T___NAME__, PNone.NO_VALUE); + if (!PyUnicodeCheckNode.executeUncached(modName)) { + return PRaiseNativeNodeGen.getUncached().raiseIntWithoutFrame(-1, SystemError, NAMELESS_MODULE, EMPTY_OBJECT_ARRAY); + } + + addMethodsToObject(functions, module, modName); + return 0; + } + + @CApiBuiltin(ret = Int, args = {PyObject, PyObject, PyMethodDef}, call = Ignored) + public static int GraalPyPrivate_AddMethodsToObject(long moduleRaw, long nameRaw, long functions) { + CompilerAsserts.neverPartOfCompilation(); + Object module = NativeToPythonInternalNode.executeUncached(moduleRaw, false); + Object name = NativeToPythonInternalNode.executeUncached(nameRaw, false); + addMethodsToObject(functions, module, name); + return 0; + } + + /** + * Implementation of {@code moduleobject.c: _add_methods_to_object}. + * + * TODO(fa): overlaps with + * {@link com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes#createLegacyMethod} + */ + private static void addMethodsToObject(long functions, Object module, Object modName) { + PythonLanguage language = PythonLanguage.get(null); + long nameRaw; + + // iterate over a native array of PyModuleDef elements + for (long def = functions; (nameRaw = readPtrField(def, CFields.PyMethodDef__ml_name)) != NULLPTR; def += CStructs.PyMethodDef.size()) { + long cfunc = readPtrField(def, CFields.PyMethodDef__ml_meth); + int flags = readIntField(def, CFields.PyMethodDef__ml_flags); + long docRaw = readPtrField(def, CFields.PyMethodDef__ml_doc); + + TruffleString name = (TruffleString) CharPtrToPythonNode.getUncached().execute(nameRaw); + Object doc = CharPtrToPythonNode.getUncached().execute(docRaw); + assert doc == PNone.NO_VALUE || doc instanceof TruffleString; + + PythonBuiltinObject func = PythonCextMethodBuiltins.cFunctionNewExMethodNode(language, def, name, cfunc, flags, module, modName, PNone.NO_VALUE, doc); + WriteAttributeToObjectNode.getUncached().execute(module, name, func); } } @CApiBuiltin(ret = Int, args = {PyObject, Pointer, Pointer}, call = Ignored) abstract static class GraalPyPrivate_Module_Traverse extends CApiTernaryBuiltinNode { - private static final String J__M_TRAVERSE = "m_traverse"; - private static final TruffleString T__M_TRAVERSE = tsLiteral(J__M_TRAVERSE); - private static final CApiTiming TIMING = CApiTiming.create(true, J__M_TRAVERSE); + private static final CApiTiming TIMING_INVOKE_TRAVERSE_PROC = CApiTiming.create(true, "invokeTraverseProc"); @Specialization - static int doGeneric(PythonModule self, Object visitFun, Object arg, + static int doGeneric(PythonModule self, long visitFun, long arg, @Bind Node inliningTarget, - @Cached CStructAccess.ReadPointerNode readPointerNode, - @Cached CStructAccess.ReadI64Node readI64Node, - @CachedLibrary(limit = "1") InteropLibrary lib, - @Cached EnsureExecutableNode ensureExecutableNode, - @Cached GetThreadStateNode getThreadStateNode, - @Cached ExternalFunctionInvokeNode externalFunctionInvokeNode, @Cached CheckPrimitiveFunctionResultNode checkPrimitiveFunctionResultNode, @Cached PythonToNativeNode toNativeNode) { @@ -282,20 +315,19 @@ static int doGeneric(PythonModule self, Object visitFun, Object arg, * As in 'moduleobject.c: module_traverse': 'if (m->md_def && m->md_def->m_traverse && * (m->md_def->m_size <= 0 || m->md_state != NULL))' */ - Object mdDef = self.getNativeModuleDef(); - if (mdDef != null) { - Object mTraverse = readPointerNode.read(mdDef, CFields.PyModuleDef__m_traverse); - if (!lib.isNull(mTraverse)) { - long mSize = readI64Node.read(mdDef, CFields.PyModuleDef__m_size); - Object mdState = self.getNativeModuleState(); - if (mSize <= 0 || (mdState != null && !lib.isNull(mdState))) { - PythonThreadState threadState = getThreadStateNode.execute(inliningTarget); - Object traverseExecutable = ensureExecutableNode.execute(inliningTarget, mTraverse, PExternalFunctionWrapper.TRAVERSEPROC); - Object res = externalFunctionInvokeNode.call(null, inliningTarget, threadState, TIMING, T__M_TRAVERSE, traverseExecutable, toNativeNode.execute(self), visitFun, arg); - int ires = (int) checkPrimitiveFunctionResultNode.executeLong(threadState, StringLiterals.T_VISIT, res); - if (ires != 0) { - return ires; - } + long mdDef = self.getNativeModuleDef(); + if (mdDef != NULLPTR) { + long mTraverse = readPtrField(mdDef, CFields.PyModuleDef__m_traverse); + if (mTraverse != NULLPTR) { + long mSize = readLongField(mdDef, CFields.PyModuleDef__m_size); + long mdState = self.getNativeModuleState(); + if (mSize <= 0 || mdState != NULLPTR) { + PythonContext ctx = PythonContext.get(inliningTarget); + NativeFunctionPointer traverseExecutable = bindFunctionPointer(mTraverse, ExternalFunctionSignature.TRAVERSEPROC); + int ires = ExternalFunctionInvoker.invokeTRAVERSEPROC(null, TIMING_INVOKE_TRAVERSE_PROC, ctx.ensureNativeContext(), BoundaryCallData.getUncached(), + ctx.getThreadState(PythonLanguage.get(inliningTarget)), traverseExecutable, toNativeNode.executeLong(self), visitFun, arg); + checkPrimitiveFunctionResultNode.executeLong(inliningTarget, ctx.getThreadState(PythonLanguage.get(inliningTarget)), StringLiterals.T_VISIT, ires); + return ires; } } } @@ -328,7 +360,7 @@ static Object error(@SuppressWarnings("unused") Object module, @CApiBuiltin(ret = ArgDescriptor.Void, args = {PyModuleObject, PyModuleDef}, call = Ignored) abstract static class GraalPyPrivate_Module_SetDef extends CApiBinaryBuiltinNode { @Specialization - static Object set(PythonModule object, Object value) { + static Object set(PythonModule object, long value) { object.setNativeModuleDef(value); return PNone.NO_VALUE; } @@ -337,7 +369,7 @@ static Object set(PythonModule object, Object value) { @CApiBuiltin(ret = ArgDescriptor.Void, args = {PyModuleObject, Pointer}, call = Ignored) abstract static class GraalPyPrivate_Module_SetState extends CApiBinaryBuiltinNode { @Specialization - static Object set(PythonModule object, Object value) { + static Object set(PythonModule object, long value) { object.setNativeModuleState(value); return PNone.NO_VALUE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java index 81a4f94c41..3f393b3324 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java @@ -51,20 +51,21 @@ 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.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.PyObjectWrapper; 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.PyVarObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_hash_t; 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.VA_LIST_PTR; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void; import static com.oracle.graal.python.builtins.objects.ints.PInt.intValue; +import static com.oracle.graal.python.builtins.objects.object.PythonObject.IMMORTAL_REFCNT; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readPtrArrayElement; import static com.oracle.graal.python.nodes.ErrorMessages.UNHASHABLE_TYPE_P; import static com.oracle.graal.python.nodes.SpecialMethodNames.T___BYTES__; import static com.oracle.graal.python.nodes.StringLiterals.T_JAVA; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import java.io.PrintWriter; +import java.lang.ref.Reference; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; @@ -85,17 +86,12 @@ import com.oracle.graal.python.builtins.objects.bytes.BytesNodes; import com.oracle.graal.python.builtins.objects.bytes.BytesUtils; import com.oracle.graal.python.builtins.objects.bytes.PBytes; -import com.oracle.graal.python.builtins.objects.cext.capi.CApiGuards; -import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.ResolvePointerNode; -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.transitions.CApiTransitions; +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.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.CApiTransitions.UpdateStrongRefNode; -import com.oracle.graal.python.builtins.objects.cext.common.GetNextVaArgNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonObjectReference; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.UpdateHandleTableReferenceNode; 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.SequenceNodes; @@ -104,6 +100,7 @@ import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode; import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.SetattrNode; +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.TypeNodes; import com.oracle.graal.python.lib.PyBytesCheckNode; @@ -145,15 +142,15 @@ import com.oracle.graal.python.runtime.sequence.storage.EmptySequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; -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.Fallback; 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.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; @@ -165,16 +162,30 @@ public abstract class PythonCextObjectBuiltins { private PythonCextObjectBuiltins() { } - @CApiBuiltin(ret = Void, args = {PyObjectWrapper, Py_ssize_t}, call = Ignored) + @CApiBuiltin(ret = Void, args = {Pointer, Py_ssize_t}, call = Ignored) abstract static class GraalPyPrivate_NotifyRefCount extends CApiBinaryBuiltinNode { @Specialization - static Object doGeneric(PythonAbstractObjectNativeWrapper wrapper, long refCount, + static Object doLong(long pointer, long refCount, @Bind Node inliningTarget, - @Cached UpdateStrongRefNode updateRefNode) { - assert CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(wrapper.getNativePointer())) == refCount; + @Cached UpdateHandleTableReferenceNode updateRefNode) { + assert HandlePointerConverter.pointsToPyHandleSpace(pointer); + assert !HandlePointerConverter.pointsToPyIntHandle(pointer); + assert !HandlePointerConverter.pointsToPyFloatHandle(pointer); + assert CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(pointer)) == refCount; // refcounting on an immortal object should be a NOP - assert refCount != PythonAbstractObjectNativeWrapper.IMMORTAL_REFCNT; - updateRefNode.execute(inliningTarget, wrapper, refCount); + assert refCount != PythonObject.IMMORTAL_REFCNT; + HandleContext handleContext = PythonContext.get(inliningTarget).handleContext; + int hti = CStructAccess.readIntField(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index); + /* + * The handle table index may be 0. This means that the managed object was already + * collected but the native companion was still not freed because its refcount was not + * 0. This can happen if the object is a GC object. For more explanation, see + * 'CApiTransitions.pollReferenceQueue' (in the branch where + * 'CFields.GraalPyObject__handle_table_index' is set to 0). + */ + if (hti != 0) { + updateRefNode.execute(inliningTarget, handleContext, pointer, hti, refCount); + } return PNone.NO_VALUE; } } @@ -183,34 +194,58 @@ static Object doGeneric(PythonAbstractObjectNativeWrapper wrapper, long refCount abstract static class GraalPyPrivate_BulkNotifyRefCount extends CApiBinaryBuiltinNode { @Specialization - static Object doGeneric(Object arrayPointer, int len, + static Object doLong(long arrayPointer, int len, @Bind Node inliningTarget, - @Cached UpdateStrongRefNode updateRefNode, - @Cached CStructAccess.ReadPointerNode readPointerNode, - @Cached ToPythonWrapperNode toPythonWrapperNode) { + @Shared @Cached UpdateHandleTableReferenceNode updateRefNode, + @Shared @Cached NativeToPythonInternalNode nativeToPythonNode) { /* * It may happen that due to several inc- and decrefs applied to a borrowed reference, * that the same pointer is in the list several times. To avoid crashes, we do the - * processing in two phases: first, we resolve the pointers to wrappers and second, we + * processing in two phases: first, we resolve the pointers to objects and second, we * update the reference counts. In this way, we avoid that a reference is made weak when * processed the first time and may then be invalid if processed the second time. */ - PythonNativeWrapper[] resolved = new PythonNativeWrapper[len]; + long[] pointers = new long[len]; + Object[] resolved = new Object[len]; for (int i = 0; i < resolved.length; i++) { - Object elem = readPointerNode.readArrayElement(arrayPointer, i); - resolved[i] = toPythonWrapperNode.executeWrapper(elem, false); + long elem = readPtrArrayElement(arrayPointer, i); + pointers[i] = elem; + resolved[i] = nativeToPythonNode.execute(inliningTarget, elem, false); } + HandleContext handleContext = PythonContext.get(inliningTarget).handleContext; for (int i = 0; i < resolved.length; i++) { - if (resolved[i] instanceof PythonAbstractObjectNativeWrapper objectNativeWrapper) { - long refCount = CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(objectNativeWrapper.getNativePointer())); - // refcounting on an immortal object should be a NOP - assert refCount != PythonAbstractObjectNativeWrapper.IMMORTAL_REFCNT; - updateRefNode.execute(inliningTarget, objectNativeWrapper, refCount); + long refCount = CApiTransitions.readNativeRefCount(pointers[i]); + // refcounting on an immortal object should be a NOP + assert refCount != PythonObject.IMMORTAL_REFCNT; + int hti = CStructAccess.readIntField(pointers[i], CFields.GraalPyObject__handle_table_index); + /* + * The handle table index may be 0. This means that the managed object was already + * collected but the native companion was still not freed because its refcount was + * not 0. This can happen if the object is a GC object. For more explanation, see + * 'CApiTransitions.pollReferenceQueue' (in the branch where + * 'CFields.GraalPyObject__handle_table_index' is set to 0). + */ + if (hti != 0) { + updateRefNode.execute(inliningTarget, handleContext, pointers[i], hti, refCount); } } + Reference.reachabilityFence(resolved); return PNone.NO_VALUE; } + + @Specialization(limit = "1") + static Object doInteropPointer(Object pointer, int len, + @Bind Node inliningTarget, + @Shared @Cached UpdateHandleTableReferenceNode updateRefNode, + @Shared @Cached NativeToPythonInternalNode nativeToPythonNode, + @CachedLibrary("pointer") InteropLibrary lib) { + try { + return doLong(lib.asPointer(pointer), len, inliningTarget, updateRefNode, nativeToPythonNode); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } } @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, PyObject, Int}, call = Ignored) @@ -233,66 +268,19 @@ static Object doGeneric(Object callable, Object argsObj, Object kwargsObj, int s } } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, VA_LIST_PTR}, call = Ignored) - abstract static class GraalPyPrivate_Object_CallFunctionObjArgs extends CApiBinaryBuiltinNode { - - @Specialization - static Object doFunction(Object callable, Object vaList, - @Bind Node inliningTarget, - @Cached GetNextVaArgNode getVaArgs, - @CachedLibrary(limit = "2") InteropLibrary argLib, - @Cached CallNode callNode, - @Cached NativeToPythonNode toJavaNode) { - return callFunction(inliningTarget, callable, vaList, getVaArgs, argLib, callNode, toJavaNode); - } - - static Object callFunction(Node inliningTarget, Object callable, Object vaList, - GetNextVaArgNode getVaArgs, - InteropLibrary argLib, - CallNode callNode, - NativeToPythonNode toJavaNode) { - /* - * Function 'PyObject_CallFunctionObjArgs' expects a va_list that contains just - * 'PyObject *' and is terminated by 'NULL'. - */ - Object[] args = new Object[4]; - int filled = 0; - while (true) { - Object object; - try { - object = getVaArgs.execute(inliningTarget, vaList); - } catch (InteropException e) { - throw CompilerDirectives.shouldNotReachHere(); - } - if (argLib.isNull(object)) { - break; - } - if (filled >= args.length) { - args = PythonUtils.arrayCopyOf(args, args.length * 2); - } - args[filled++] = toJavaNode.execute(object); - } - if (filled < args.length) { - args = PythonUtils.arrayCopyOf(args, filled); - } - return callNode.executeWithoutFrame(callable, args); - } - } - - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, VA_LIST_PTR}, call = Ignored) - abstract static class GraalPyPrivate_Object_CallMethodObjArgs extends CApiTernaryBuiltinNode { + @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject, PyObjectConstPtr, Py_ssize_t}, call = Ignored) + abstract static class GraalPyPrivate_Object_CallMethodObjArgs extends CApiQuaternaryBuiltinNode { @Specialization - static Object doMethod(Object receiver, Object methodName, Object vaList, + static Object doMethod(Object receiver, Object methodName, long argsArray, long nargs, @Bind Node inliningTarget, - @Cached GetNextVaArgNode getVaArgs, - @CachedLibrary(limit = "2") InteropLibrary argLib, - @Cached CallNode callNode, @Cached PyObjectGetAttrO getAnyAttributeNode, - @Cached NativeToPythonNode toJavaNode) { + @Cached CStructAccess.ReadObjectNode readNode, + @Cached CallNode callNode) { Object method = getAnyAttributeNode.execute(null, inliningTarget, receiver, methodName); - return GraalPyPrivate_Object_CallFunctionObjArgs.callFunction(inliningTarget, method, vaList, getVaArgs, argLib, callNode, toJavaNode); + Object[] args = readNode.readPyObjectArray(argsArray, (int) nargs); + return callNode.executeWithoutFrame(method, args); } } @@ -319,7 +307,7 @@ static Object doGeneric(Object receiver, TruffleString methodName, Object argsOb abstract static class _PyObject_MakeTpCall extends CApi5BuiltinNode { @Specialization - static Object doGeneric(@SuppressWarnings("unused") Object threadState, Object callable, Object argsArray, long nargs, Object kwargs, + static Object doGeneric(@SuppressWarnings("unused") long threadState, Object callable, long argsArray, long nargs, Object kwargs, @Cached CStructAccess.ReadObjectNode readNode, @Bind Node inliningTarget, @Cached CStructAccess.ReadObjectNode readKwNode, @@ -491,7 +479,7 @@ static int hasAttr(Object obj, Object attr, @CApiBuiltin(ret = Py_hash_t, args = {PyObject}, call = Direct) abstract static class PyObject_HashNotImplemented extends CApiUnaryBuiltinNode { @Specialization - static Object unhashable(Object obj, + static long unhashable(Object obj, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, UNHASHABLE_TYPE_P, obj); } @@ -601,54 +589,54 @@ static PNone set(PSequence obj, long size, @CApiBuiltin(ret = Int, args = {PyObjectRawPointer}, call = Ignored) abstract static class GraalPyPrivate_Object_IsFreed extends CApiUnaryBuiltinNode { @Specialization - int doGeneric(Object pointer, - @Cached ToPythonWrapperNode toPythonWrapperNode) { - return toPythonWrapperNode.executeWrapper(pointer, false) == null ? 1 : 0; + static int doLong(long pointer, + @Bind Node inliningTarget) { + assert !HandlePointerConverter.pointsToPyIntHandle(pointer); + assert !HandlePointerConverter.pointsToPyFloatHandle(pointer); + int handleTableIndex = CStructAccess.readIntField(HandlePointerConverter.pointerToStub(pointer), CFields.GraalPyObject__handle_table_index); + Object reference = CApiTransitions.nativeStubLookupGet(PythonContext.get(inliningTarget).handleContext, pointer, handleTableIndex); + Object referent; + if (reference instanceof PythonObjectReference pythonObjectReference) { + assert handleTableIndex == pythonObjectReference.getHandleTableIndex(); + referent = pythonObjectReference.get(); + } else { + referent = reference; + } + return referent == null ? 1 : 0; } } - @CApiBuiltin(ret = Void, args = {PyObjectWrapper}, call = Ignored) + @CApiBuiltin(ret = Void, args = {Pointer}, call = Ignored) abstract static class GraalPyPrivate_Object_Dump extends CApiUnaryBuiltinNode { @Specialization @TruffleBoundary - int doGeneric(Object ptrObject, - @Cached CStructAccess.ReadI64Node readI64) { - PythonContext context = getContext(); + static int doGeneric(long pointer) { + PythonContext context = PythonContext.get(null); PrintWriter stderr = new PrintWriter(context.getStandardErr()); - // There are three cases we need to distinguish: - // 1) The pointer object is a native pointer and is NOT a handle - // 2) The pointer object is a native pointer and is a handle - // 3) The pointer object is one of our native wrappers - - boolean isWrapper = CApiGuards.isNativeWrapper(ptrObject); - /* * At this point we don't know if the pointer is invalid, so we try to resolve it to an * object. */ - Object resolved = isWrapper ? ptrObject : ResolvePointerNode.executeUncached(ptrObject); - Object pythonObject; + Object resolved = NativeToPythonInternalNode.executeUncached(pointer, false); + + // There are two cases we need to distinguish: + // 1) The pointer object is a native pointer and is NOT a handle + // 2) The pointer object is a native pointer and is a handle long refCnt; - // We need again check if 'resolved' is a wrapper in case we resolved a handle. - if (resolved instanceof PythonAbstractObjectNativeWrapper objectNativeWrapper) { - if (objectNativeWrapper.isNative()) { - refCnt = objectNativeWrapper.getRefCount(); - } else { - refCnt = PythonAbstractObjectNativeWrapper.MANAGED_REFCNT; - } + if (HandlePointerConverter.pointsToPyIntHandle(pointer) || HandlePointerConverter.pointsToPyFloatHandle(pointer)) { + refCnt = IMMORTAL_REFCNT; } else { - refCnt = readI64.read(PythonToNativeNode.executeUncached(resolved), CFields.PyObject__ob_refcnt); + refCnt = CApiTransitions.readNativeRefCount(HandlePointerConverter.pointsToPyHandleSpace(pointer) ? HandlePointerConverter.pointerToStub(pointer) : pointer); } - pythonObject = NativeToPythonNode.executeUncached(ptrObject); // first, write fields which are the least likely to crash - stderr.println("ptrObject address : " + ptrObject); + stderr.println("ptrObject address : 0x" + Long.toHexString(pointer)); stderr.println("ptrObject refcount : " + refCnt); stderr.flush(); - Object type = GetClassNode.executeUncached(pythonObject); + Object type = GetClassNode.executeUncached(resolved); stderr.println("object type : " + type); stderr.println("object type name: " + TypeNodes.GetNameNode.executeUncached(type)); @@ -656,7 +644,7 @@ int doGeneric(Object ptrObject, stderr.println("object repr : "); stderr.flush(); try { - Object reprObj = PyObjectCallMethodObjArgs.executeUncached(context.getBuiltins(), BuiltinNames.T_REPR, pythonObject); + Object reprObj = PyObjectCallMethodObjArgs.executeUncached(context.getBuiltins(), BuiltinNames.T_REPR, resolved); stderr.println(CastToJavaStringNode.getUncached().execute(reprObj)); } catch (PException | CannotCastException e) { // errors are ignored at this point diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyLifecycleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyLifecycleBuiltins.java index 9ff484529c..2d10e11f19 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyLifecycleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyLifecycleBuiltins.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,14 +46,8 @@ 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.runtime.PythonContext; -import com.oracle.graal.python.util.ShutdownHook; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; 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; public final class PythonCextPyLifecycleBuiltins { @@ -62,17 +56,15 @@ abstract static class Py_AtExit extends CApiUnaryBuiltinNode { @Specialization @TruffleBoundary - int doGeneric(Object funcPtr) { - getContext().registerAtexitHook(new ShutdownHook() { - @Override - public void call(@SuppressWarnings("unused") PythonContext context) { - try { - InteropLibrary.getUncached().execute(funcPtr); - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - // ignored - } - } - }); + int doGeneric(@SuppressWarnings("unused") long funcPtr) { + // TODO(native-access) implement and test this once GR-72092 is fixed +// getContext().registerAtexitHook(new ShutdownHook() { +// @Override +// @TruffleBoundary +// public void call(PythonContext context) { +// CALLBACK_SIGNATURE.invoke(context.ensureNativeContext(), funcPtr); +// } +// }); return 0; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyStateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyStateBuiltins.java index 872840159d..de56b02784 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyStateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyStateBuiltins.java @@ -50,6 +50,8 @@ 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.Py_ssize_t; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Void; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin; @@ -124,7 +126,7 @@ abstract static class GraalPyPrivate_ThreadState_Get extends CApiUnaryBuiltinNod @Specialization @TruffleBoundary - static Object get(Object tstateCurrentPtr) { + static long get(long tstateCurrentPtr) { PythonContext context = PythonContext.get(null); PythonThreadState threadState = context.getThreadState(context.getLanguage()); @@ -136,14 +138,14 @@ static Object get(Object tstateCurrentPtr) { */ if (threadState.isNativeThreadStateInitialized()) { LOGGER.fine(() -> String.format("Lazy initialization attempt of native thread state for thread %s aborted. Was initialized in the meantime.", Thread.currentThread())); - Object nativeThreadState = PThreadState.getNativeThreadState(threadState); - assert nativeThreadState != null; + long nativeThreadState = threadState.getNativePointer(); + assert nativeThreadState != NULLPTR; return nativeThreadState; } LOGGER.fine(() -> "Lazy (fallback) initialization of native thread state for thread " + Thread.currentThread()); - assert PThreadState.getNativeThreadState(threadState) == null; - Object nativeThreadState = PThreadState.getOrCreateNativeThreadState(threadState); + assert threadState.getNativePointer() == NULLPTR; + long nativeThreadState = PThreadState.getOrCreateNativeThreadState(threadState); threadState.setNativeThreadLocalVarPointer(tstateCurrentPtr); return nativeThreadState; } @@ -227,10 +229,10 @@ protected void perform(Access access) { @CApiBuiltin(ret = PyFrameObjectTransfer, args = {PyThreadState}, call = Direct) abstract static class PyThreadState_GetFrame extends CApiUnaryBuiltinNode { @Specialization - Object get(@SuppressWarnings("unused") Object threadState, + Object get(@SuppressWarnings("unused") long threadState, @Cached ReadFrameNode readFrameNode) { PFrame pFrame = readFrameNode.getCurrentPythonFrame(null); - return pFrame != null ? pFrame : getNativeNull(); + return pFrame != null ? pFrame : NATIVE_NULL; } } @@ -243,11 +245,11 @@ Object doGeneric(long mIndex) { int i = PInt.intValueExact(mIndex); Object result = getCApiContext().getModuleByIndex(i); if (result == null) { - return getNativeNull(); + return NATIVE_NULL; } return result; } catch (CannotCastException | OverflowException e) { - return getNativeNull(); + return NATIVE_NULL; } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyThreadBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyThreadBuiltins.java index 7ed4663ea5..6b67216ec3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyThreadBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPyThreadBuiltins.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 @@ -43,6 +43,7 @@ import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; 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.Long; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_THREAD_TYPE_LOCK; 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.UNSIGNED_LONG; @@ -50,139 +51,76 @@ import static com.oracle.graal.python.builtins.objects.ints.PInt.intValue; 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.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.modules.cext.PythonCextBuiltins.CApiBuiltinNode; import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext; -import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor; import com.oracle.graal.python.builtins.objects.thread.PLock; 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.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.EncapsulatingNodeReference; public final class PythonCextPyThreadBuiltins { private static final long LOCK_MASK = 0xA10C000000000000L; - @CApiBuiltin(ret = PY_THREAD_TYPE_LOCK, args = {}, call = Direct) - abstract static class PyThread_allocate_lock extends CApiNullaryBuiltinNode { - @Specialization - @TruffleBoundary - long allocate() { - CApiContext context = getCApiContext(); - long id = context.lockId.incrementAndGet() ^ LOCK_MASK; - PLock lock = PFactory.createLock(PythonLanguage.get(null)); - context.locks.put(id, lock); - return id; - } + @CApiBuiltin(ret = PY_THREAD_TYPE_LOCK, args = {}, call = Direct, acquireGil = false, canRaise = false) + public static long PyThread_allocate_lock() { + CApiContext context = CApiBuiltinNode.getStaticCApiContext(); + long id = context.lockId.incrementAndGet() ^ LOCK_MASK; + PLock lock = PFactory.createLock(PythonLanguage.get(null)); + context.locks.put(id, lock); + return id; } - @CApiBuiltin(ret = Int, args = {PY_THREAD_TYPE_LOCK, Int}, call = Direct) - abstract static class PyThread_acquire_lock extends CApiBinaryBuiltinNode { - @Specialization - @TruffleBoundary - int acquire(long id, int waitflag) { - PLock lock = getCApiContext().locks.get(id); - if (lock == null) { - throw badInternalCall("lock"); - } - boolean result; - // N.B: Cannot use AcquireNode because we may be running without a GIL - if (waitflag != 0) { - result = lock.acquireBlocking(this); - } else { - result = lock.acquireNonBlocking(); - } - return intValue(result); + @CApiBuiltin(ret = Int, args = {PY_THREAD_TYPE_LOCK, Int}, call = Direct, acquireGil = false, canRaise = true) + public static int PyThread_acquire_lock(long id, int waitflag) { + CApiContext context = CApiBuiltinNode.getStaticCApiContext(); + PLock lock = context.locks.get(id); + if (lock == null) { + throw PythonCextBuiltins.badInternalCall("PyThread_acquire_lock", "lock"); } - - @Specialization(guards = "lib.isPointer(id)", limit = "1") - int acquire(Object id, int waitflag, - @CachedLibrary("id") InteropLibrary lib) { - try { - return acquire(lib.asPointer(id), waitflag); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(); - } + boolean result; + if (waitflag != 0) { + result = lock.acquireBlocking(EncapsulatingNodeReference.getCurrent().get()); + } else { + result = lock.acquireNonBlocking(); } + return intValue(result); } - @CApiBuiltin(ret = Void, args = {PY_THREAD_TYPE_LOCK}, call = Direct) - abstract static class PyThread_release_lock extends CApiUnaryBuiltinNode { - @Specialization - @TruffleBoundary - Object release(long id) { - CApiContext context = getCApiContext(); - PLock lock = context.locks.get(id); - if (lock == null) { - throw badInternalCall("lock"); - } - lock.release(); - return PNone.NO_VALUE; - } - - @Specialization(guards = "lib.isPointer(id)", limit = "1") - Object release(Object id, - @CachedLibrary("id") InteropLibrary lib) { - try { - return release(lib.asPointer(id)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(); - } + @CApiBuiltin(ret = Void, args = {PY_THREAD_TYPE_LOCK}, call = Direct, acquireGil = false, canRaise = true) + public static void PyThread_release_lock(long id) { + CApiContext context = CApiBuiltinNode.getStaticCApiContext(); + PLock lock = context.locks.get(id); + if (lock == null) { + throw PythonCextBuiltins.badInternalCall("PyThread_release_lock", "lock"); } + lock.release(); } - @CApiBuiltin(ret = ArgDescriptor.Long, args = {}, call = Ignored) - abstract static class GraalPyPrivate_tss_create extends CApiNullaryBuiltinNode { - @Specialization - @TruffleBoundary - long tssCreate() { - return getCApiContext().nextTssKey(); - } + @CApiBuiltin(ret = Long, args = {}, call = Ignored, acquireGil = false, canRaise = false) + public static long GraalPyPrivate_tss_create() { + return CApiBuiltinNode.getStaticCApiContext().nextTssKey(); } - @CApiBuiltin(ret = Pointer, args = {ArgDescriptor.Long}, call = Ignored) - abstract static class GraalPyPrivate_tss_get extends CApiUnaryBuiltinNode { - @Specialization - Object tssGet(long key) { - Object value = getCApiContext().tssGet(key); - if (value == null) { - return getNULL(); - } - return value; - } + @CApiBuiltin(ret = Pointer, args = {Long}, call = Ignored, acquireGil = false, canRaise = false) + public static long GraalPyPrivate_tss_get(long key) { + return CApiBuiltinNode.getStaticCApiContext().tssGet(key); } - @CApiBuiltin(ret = Int, args = {ArgDescriptor.Long, Pointer}, call = Ignored) - abstract static class GraalPyPrivate_tss_set extends CApiBinaryBuiltinNode { - @Specialization - int tssSet(long key, Object value) { - getCApiContext().tssSet(key, value); - return 0; - } + @CApiBuiltin(ret = Int, args = {Long, Pointer}, call = Ignored, acquireGil = false, canRaise = false) + public static int GraalPyPrivate_tss_set(long key, long value) { + CApiBuiltinNode.getStaticCApiContext().tssSet(key, value); + return 0; } - @CApiBuiltin(ret = Void, args = {ArgDescriptor.Long}, call = Ignored) - abstract static class GraalPyPrivate_tss_delete extends CApiUnaryBuiltinNode { - @Specialization - Object tssDelete(long key) { - getCApiContext().tssDelete(key); - return PNone.NONE; - } + @CApiBuiltin(ret = Void, args = {Long}, call = Ignored, acquireGil = false, canRaise = false) + public static void GraalPyPrivate_tss_delete(long key) { + CApiBuiltinNode.getStaticCApiContext().tssDelete(key); } - @CApiBuiltin(ret = UNSIGNED_LONG, args = {}, call = Direct) - abstract static class PyThread_get_thread_ident extends CApiNullaryBuiltinNode { - @SuppressWarnings("deprecation") // deprecated in JDK19 - @Specialization - long get() { - return Thread.currentThread().getId(); - } + @SuppressWarnings("deprecation") // deprecated in JDK19 + @CApiBuiltin(ret = UNSIGNED_LONG, args = {}, call = Direct, acquireGil = false, canRaise = false) + public static long PyThread_get_thread_ident() { + return Thread.currentThread().getId(); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPythonRunBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPythonRunBuiltins.java index ea2205c13b..b9739c3c26 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPythonRunBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextPythonRunBuiltins.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 @@ -48,6 +48,7 @@ import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PY_COMPILER_FLAGS; 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.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.nodes.BuiltinNames.T_COMPILE; import static com.oracle.graal.python.nodes.BuiltinNames.T_EVAL; import static com.oracle.graal.python.nodes.BuiltinNames.T_EXEC; @@ -83,7 +84,7 @@ public final class PythonCextPythonRunBuiltins { abstract static class PyRun_StringFlags extends CApi5BuiltinNode { @Specialization(guards = "checkArgs(source, globals, locals, inliningTarget, isMapping)", limit = "1") - static Object run(Object source, int type, Object globals, Object locals, @SuppressWarnings("unused") Object flags, + static Object run(Object source, int type, Object globals, Object locals, @SuppressWarnings("unused") long flags, @Bind Node inliningTarget, @SuppressWarnings("unused") @Exclusive @Cached PyMappingCheckNode isMapping, @Cached PyObjectLookupAttr lookupNode, @@ -108,7 +109,7 @@ static Object run(Object source, int type, Object globals, Object locals, @Suppr @Specialization(guards = "!isString(source) || !isDict(globals)") static Object run(@SuppressWarnings("unused") Object source, @SuppressWarnings("unused") int type, @SuppressWarnings("unused") Object globals, - @SuppressWarnings("unused") Object locals, @SuppressWarnings("unused") Object flags, + @SuppressWarnings("unused") Object locals, @SuppressWarnings("unused") long flags, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, SystemError, BAD_ARG_TO_INTERNAL_FUNC); } @@ -134,7 +135,7 @@ static Object compile(Object source, Object filename, int type, @Cached PRaiseNode raiseNode, @Cached PyObjectLookupAttr lookupNode, @Cached CallNode callNode) { - return Py_CompileStringExFlags.compile(source, filename, type, null, -1, inliningTarget, raiseNode, lookupNode, callNode); + return Py_CompileStringExFlags.compile(source, filename, type, NULLPTR, -1, inliningTarget, raiseNode, lookupNode, callNode); } @SuppressWarnings("unused") @@ -149,7 +150,7 @@ static Object fail(Object source, Object filename, Object type, abstract static class Py_CompileStringExFlags extends CApi5BuiltinNode { @Specialization(guards = {"isString(source)", "isString(filename)"}) static Object compile(Object source, Object filename, int type, - @SuppressWarnings("unused") Object flags, int optimizationLevel, + @SuppressWarnings("unused") long flags, int optimizationLevel, @Bind Node inliningTarget, @Cached PRaiseNode raiseNode, @Cached PyObjectLookupAttr lookupNode, @@ -173,7 +174,7 @@ static Object compile(Object source, Object filename, int type, @SuppressWarnings("unused") @Specialization(guards = "!isString(source) || !isString(filename)") - static Object fail(Object source, Object filename, Object type, Object flags, Object optimizationLevel, + static Object fail(Object source, Object filename, Object type, long flags, Object optimizationLevel, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, SystemError, BAD_ARG_TO_INTERNAL_FUNC); } @@ -183,18 +184,18 @@ static Object fail(Object source, Object filename, Object type, Object flags, Ob abstract static class Py_CompileStringObject extends CApi5BuiltinNode { @Specialization(guards = "isString(source)") static Object compile(Object source, Object filename, int type, - @SuppressWarnings("unused") Object flags, + @SuppressWarnings("unused") long flags, int optimizationLevel, @Bind Node inliningTarget, @Cached PRaiseNode raiseNode, @Cached PyObjectLookupAttr lookupNode, @Cached CallNode callNode) { - return Py_CompileStringExFlags.compile(source, filename, type, null, optimizationLevel, inliningTarget, raiseNode, lookupNode, callNode); + return Py_CompileStringExFlags.compile(source, filename, type, NULLPTR, optimizationLevel, inliningTarget, raiseNode, lookupNode, callNode); } @SuppressWarnings("unused") @Specialization(guards = "!isString(source)") - static Object fail(Object source, Object filename, Object type, Object flags, Object optimizationLevel, + static Object fail(Object source, Object filename, Object type, long flags, Object optimizationLevel, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, SystemError, BAD_ARG_TO_INTERNAL_FUNC); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSetBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSetBuiltins.java index d6e4d43ec0..f72dbdd14e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSetBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSetBuiltins.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,6 +50,7 @@ import static com.oracle.graal.python.nodes.ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC_WAS_S_P; import static com.oracle.graal.python.nodes.ErrorMessages.EXPECTED_S_NOT_P; import static com.oracle.graal.python.nodes.ErrorMessages.NATIVE_S_SUBTYPES_NOT_IMPLEMENTED; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; @@ -162,7 +163,7 @@ static Object nextEntry(PFrozenSet set, long pos, static Object nextEntry(@SuppressWarnings("unused") Object set, @SuppressWarnings("unused") long pos, @Bind Node inliningTarget, @SuppressWarnings("unused") @Shared @Cached PyObjectSizeNode sizeNode) { - return getNativeNull(inliningTarget); + return NATIVE_NULL; } @Specialization(guards = {"!isPSet(anyset)", "!isPFrozenSet(anyset)", "isSetSubtype(inliningTarget, anyset, getClassNode, isSubtypeNode)"}) @@ -198,7 +199,7 @@ private static Object next(Node inliningTarget, int pos, HashingStorage storage, loopProfile.profileCounted(inliningTarget, pos); for (int i = 0; loopProfile.inject(inliningTarget, i <= pos); i++) { if (!itNext.execute(inliningTarget, storage, it)) { - return getNativeNull(inliningTarget); + return NATIVE_NULL; } } Object key = itKey.execute(inliningTarget, storage, it); @@ -296,7 +297,7 @@ static long get(PBaseSet object, } @Fallback - static int error(@SuppressWarnings("unused") Object self, + static long error(@SuppressWarnings("unused") Object self, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSlotBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSlotBuiltins.java index 26569dd66a..2900e991fb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSlotBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSlotBuiltins.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 @@ -62,7 +62,6 @@ 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.PyObjectPtr; -import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectWrapper; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PySetObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PySliceObject; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTupleObject; @@ -76,11 +75,14 @@ import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.getter; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.setter; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.vectorcallfunc; +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.nodes.HiddenAttr.METHOD_DEF_PTR; import static com.oracle.graal.python.nodes.HiddenAttr.PROMOTED_START; import static com.oracle.graal.python.nodes.HiddenAttr.PROMOTED_STEP; import static com.oracle.graal.python.nodes.HiddenAttr.PROMOTED_STOP; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import com.oracle.graal.python.builtins.PythonBuiltinClassType; @@ -91,11 +93,12 @@ import com.oracle.graal.python.builtins.objects.bytes.PByteArray; import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes; -import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.AsCharPointerNode; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.ObSizeNode; import com.oracle.graal.python.builtins.objects.cext.capi.PyMethodDefHelper; import com.oracle.graal.python.builtins.objects.cext.capi.PySequenceArrayWrapper; -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.HandlePointerConverter; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageLen; import com.oracle.graal.python.builtins.objects.frame.FrameBuiltins; @@ -103,27 +106,26 @@ import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; import com.oracle.graal.python.builtins.objects.getsetdescriptor.GetSetDescriptor; import com.oracle.graal.python.builtins.objects.ints.PInt; +import com.oracle.graal.python.builtins.objects.list.PList; import com.oracle.graal.python.builtins.objects.method.PBuiltinMethod; import com.oracle.graal.python.builtins.objects.method.PDecoratedMethod; import com.oracle.graal.python.builtins.objects.method.PMethod; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; -import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.set.PBaseSet; import com.oracle.graal.python.builtins.objects.slice.PSlice; import com.oracle.graal.python.builtins.objects.str.NativeStringData; 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.StringNodes.StringLenNode; +import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectSetAttr; import com.oracle.graal.python.nodes.HiddenAttr; -import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.PythonContext; -import com.oracle.graal.python.runtime.sequence.PSequence; import com.oracle.graal.python.runtime.sequence.storage.NativeByteSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.NativeObjectSequenceStorage; import com.oracle.truffle.api.CompilerDirectives; @@ -139,15 +141,18 @@ public final class PythonCextSlotBuiltins { - @CApiBuiltin(name = "GraalPyPrivate_Get_PyListObject_ob_item", ret = PyObjectPtr, args = {PyListObject}, call = Ignored) - @CApiBuiltin(name = "GraalPyPrivate_Get_PyTupleObject_ob_item", ret = PyObjectPtr, args = {PyTupleObject}, call = Ignored) - abstract static class GraalPyPrivate_Get_PSequence_ob_item extends CApiUnaryBuiltinNode { + @CApiBuiltin(ret = PyObjectPtr, args = {PyListObject}, call = Ignored) + public static long GraalPyPrivate_Get_PyListObject_ob_item(long ptr) { + PList object = (PList) NativeToPythonInternalNode.executeUncached(ptr, false); + assert !(object.getSequenceStorage() instanceof NativeByteSequenceStorage); + return PySequenceArrayWrapper.ensureNativeSequence(object); + } - @Specialization - static Object get(PSequence object) { - assert !(object.getSequenceStorage() instanceof NativeByteSequenceStorage); - return PySequenceArrayWrapper.ensureNativeSequence(object); - } + @CApiBuiltin(ret = PyObjectPtr, args = {PyTupleObject}, call = Ignored) + public static long GraalPyPrivate_Get_PyTupleObject_ob_item(long ptr) { + PTuple object = (PTuple) NativeToPythonInternalNode.executeUncached(ptr, false); + assert !(object.getSequenceStorage() instanceof NativeByteSequenceStorage); + return PySequenceArrayWrapper.ensureNativeSequence(object); } @CApiBuiltin(ret = Py_ssize_t, args = {PyASCIIObject}, call = Ignored) @@ -246,9 +251,9 @@ static Object get(PBuiltinMethod object) { @CApiBuiltin(ret = PyMethodDef, args = {PyCFunctionObject}, call = Ignored) abstract static class GraalPyPrivate_Get_PyCFunctionObject_m_ml extends CApiUnaryBuiltinNode { @Specialization - static Object get(PythonBuiltinObject object, + static long get(PythonBuiltinObject object, @Bind Node inliningTarget, - @Cached HiddenAttr.ReadNode readNode) { + @Cached HiddenAttr.ReadLongNode readNode) { PBuiltinFunction resolved; if (object instanceof PBuiltinMethod builtinMethod) { resolved = builtinMethod.getBuiltinFunction(); @@ -258,8 +263,8 @@ static Object get(PythonBuiltinObject object, CompilerDirectives.transferToInterpreterAndInvalidate(); throw CompilerDirectives.shouldNotReachHere("requesting PyMethodDef for an incompatible function/method type: " + object.getClass().getSimpleName()); } - Object methodDefPtr = readNode.execute(inliningTarget, resolved, METHOD_DEF_PTR, null); - if (methodDefPtr != null) { + long methodDefPtr = readNode.execute(inliningTarget, resolved, METHOD_DEF_PTR, NULLPTR); + if (methodDefPtr != NULLPTR) { return methodDefPtr; } CApiContext cApiContext = getCApiContext(inliningTarget); @@ -270,9 +275,9 @@ static Object get(PythonBuiltinObject object, @CApiBuiltin(ret = PyMethodDef, args = {PyCFunctionObject, PyMethodDef}, call = Ignored) abstract static class GraalPyPrivate_Set_PyCFunctionObject_m_ml extends CApiBinaryBuiltinNode { @Specialization - static Object get(PythonBuiltinObject object, Object methodDefPtr, + static long get(PythonBuiltinObject object, long methodDefPtr, @Bind Node inliningTarget, - @Cached HiddenAttr.WriteNode writeNode) { + @Cached HiddenAttr.WriteLongNode writeNode) { PBuiltinFunction resolved; if (object instanceof PBuiltinMethod builtinMethod) { resolved = builtinMethod.getBuiltinFunction(); @@ -283,7 +288,7 @@ static Object get(PythonBuiltinObject object, Object methodDefPtr, throw CompilerDirectives.shouldNotReachHere("writing PyMethodDef for an incompatible function/method type: " + object.getClass().getSimpleName()); } writeNode.execute(inliningTarget, resolved, METHOD_DEF_PTR, methodDefPtr); - return PNone.NO_VALUE; + return NULLPTR; } } @@ -294,7 +299,7 @@ Object get(Object object, @Bind Node inliningTarget, @Cached PyObjectLookupAttr lookup) { Object module = lookup.execute(null, inliningTarget, object, T___MODULE__); - return module != PNone.NO_VALUE ? module : getNativeNull(); + return module != PNone.NO_VALUE ? module : NATIVE_NULL; } } @@ -333,7 +338,7 @@ static int get(@SuppressWarnings("unused") Object object) { @CApiBuiltin(ret = vectorcallfunc, args = {PyCFunctionObject}, call = Ignored) abstract static class GraalPyPrivate_Get_PyCFunctionObject_vectorcall extends CApiUnaryBuiltinNode { @Specialization - static int get(@SuppressWarnings("unused") Object object) { + static long get(@SuppressWarnings("unused") Object object) { throw CompilerDirectives.shouldNotReachHere(); } } @@ -342,7 +347,7 @@ static int get(@SuppressWarnings("unused") Object object) { abstract static class GraalPyPrivate_Get_PyByteArrayObject_ob_start extends CApiUnaryBuiltinNode { @Specialization - static Object doObStart(PByteArray object) { + static long doObStart(PByteArray object) { assert !(object.getSequenceStorage() instanceof NativeObjectSequenceStorage); return PySequenceArrayWrapper.ensureNativeSequence(object); } @@ -387,7 +392,7 @@ abstract static class GraalPyPrivate_Get_PyDescrObject_d_type extends CApiUnaryB @Specialization Object get(PBuiltinFunction object) { Object enclosingType = object.getEnclosingType(); - return enclosingType != null ? enclosingType : getNativeNull(); + return enclosingType != null ? enclosingType : NATIVE_NULL; } @Specialization @@ -408,7 +413,7 @@ static Object get(PFrame frame, @CApiBuiltin(ret = Pointer, args = {PyGetSetDef}, call = Ignored) abstract static class GraalPyPrivate_Get_PyGetSetDef_closure extends CApiUnaryBuiltinNode { @Specialization - static int get(@SuppressWarnings("unused") Object object) { + static long get(@SuppressWarnings("unused") long object) { throw CompilerDirectives.shouldNotReachHere(); } } @@ -416,22 +421,15 @@ static int get(@SuppressWarnings("unused") Object object) { @CApiBuiltin(ret = ConstCharPtrAsTruffleString, args = {PyGetSetDef}, call = Ignored) abstract static class GraalPyPrivate_Get_PyGetSetDef_doc extends CApiUnaryBuiltinNode { @Specialization - Object get(PythonObject object, - @Cached(parameters = "T___DOC__") GetFixedAttributeNode getAttrNode, - @Cached AsCharPointerNode asCharPointerNode) { - Object doc = getAttrNode.execute(null, object); - if (PGuards.isPNone(doc)) { - return getNULL(); - } else { - return asCharPointerNode.execute(doc); - } + long get(@SuppressWarnings("unused") long object) { + throw CompilerDirectives.shouldNotReachHere(); } } @CApiBuiltin(ret = getter, args = {PyGetSetDef}, call = Ignored) abstract static class GraalPyPrivate_Get_PyGetSetDef_get extends CApiUnaryBuiltinNode { @Specialization - static int get(@SuppressWarnings("unused") Object object) { + static long get(@SuppressWarnings("unused") long object) { throw CompilerDirectives.shouldNotReachHere(); } } @@ -439,22 +437,15 @@ static int get(@SuppressWarnings("unused") Object object) { @CApiBuiltin(ret = ConstCharPtrAsTruffleString, args = {PyGetSetDef}, call = Ignored) abstract static class GraalPyPrivate_Get_PyGetSetDef_name extends CApiUnaryBuiltinNode { @Specialization - Object get(PythonObject object, - @Cached(parameters = "T___NAME__") GetFixedAttributeNode getAttrNode, - @Cached AsCharPointerNode asCharPointerNode) { - Object name = getAttrNode.execute(null, object); - if (PGuards.isPNone(name)) { - return getNULL(); - } else { - return asCharPointerNode.execute(name); - } + long get(@SuppressWarnings("unused") long object) { + throw CompilerDirectives.shouldNotReachHere(); } } @CApiBuiltin(ret = setter, args = {PyGetSetDef}, call = Ignored) abstract static class GraalPyPrivate_Get_PyGetSetDef_set extends CApiUnaryBuiltinNode { @Specialization - static int get(@SuppressWarnings("unused") Object object) { + static long get(@SuppressWarnings("unused") long object) { throw CompilerDirectives.shouldNotReachHere(); } } @@ -463,11 +454,11 @@ static int get(@SuppressWarnings("unused") Object object) { abstract static class GraalPyPrivate_Get_PyMethodDescrObject_d_method extends CApiUnaryBuiltinNode { @Specialization - static Object get(PBuiltinFunction builtinFunction, + static long get(PBuiltinFunction builtinFunction, @Bind Node inliningTarget, - @Cached HiddenAttr.ReadNode readNode) { - Object methodDefPtr = readNode.execute(inliningTarget, builtinFunction, METHOD_DEF_PTR, null); - if (methodDefPtr != null) { + @Cached HiddenAttr.ReadLongNode readNode) { + long methodDefPtr = readNode.execute(inliningTarget, builtinFunction, METHOD_DEF_PTR, NULLPTR); + if (methodDefPtr != NULLPTR) { return methodDefPtr; } /* @@ -524,7 +515,7 @@ static int get(@SuppressWarnings("unused") Object object) { @CApiBuiltin(ret = PyMethodDef, args = {PyModuleDef}, call = Ignored) abstract static class GraalPyPrivate_Get_PyModuleDef_m_methods extends CApiUnaryBuiltinNode { @Specialization - static int get(@SuppressWarnings("unused") Object object) { + static int get(@SuppressWarnings("unused") long object) { throw CompilerDirectives.shouldNotReachHere(); } } @@ -532,7 +523,7 @@ static int get(@SuppressWarnings("unused") Object object) { @CApiBuiltin(ret = ConstCharPtrAsTruffleString, args = {PyModuleDef}, call = Ignored) abstract static class GraalPyPrivate_Get_PyModuleDef_m_name extends CApiUnaryBuiltinNode { @Specialization - static int get(@SuppressWarnings("unused") Object object) { + static int get(@SuppressWarnings("unused") long object) { throw CompilerDirectives.shouldNotReachHere(); } } @@ -540,7 +531,7 @@ static int get(@SuppressWarnings("unused") Object object) { @CApiBuiltin(ret = Py_ssize_t, args = {PyModuleDef}, call = Ignored) abstract static class GraalPyPrivate_Get_PyModuleDef_m_size extends CApiUnaryBuiltinNode { @Specialization - static int get(@SuppressWarnings("unused") Object object) { + static long get(@SuppressWarnings("unused") long object) { throw CompilerDirectives.shouldNotReachHere(); } } @@ -548,7 +539,7 @@ static int get(@SuppressWarnings("unused") Object object) { @CApiBuiltin(ret = PyModuleDef, args = {PyModuleObject}, call = Ignored) abstract static class GraalPyPrivate_Get_PyModuleObject_md_def extends CApiUnaryBuiltinNode { @Specialization - static Object get(PythonModule object) { + static long get(PythonModule object) { return object.getNativeModuleDef(); } } @@ -565,24 +556,23 @@ static Object get(Object object, @CApiBuiltin(ret = Pointer, args = {PyModuleObject}, call = Ignored) abstract static class GraalPyPrivate_Get_PyModuleObject_md_state extends CApiUnaryBuiltinNode { @Specialization - static Object get(PythonModule object, - @Bind Node inliningTarget) { - return object.getNativeModuleState() != null ? object.getNativeModuleState() : PythonContext.get(inliningTarget).getNativeNull(); + static Object get(PythonModule object) { + return object.getNativeModuleState(); } } - @CApiBuiltin(ret = Py_ssize_t, args = {PyObjectWrapper}, call = Ignored) + @CApiBuiltin(ret = Py_ssize_t, args = {Pointer}, call = Ignored) abstract static class GraalPyPrivate_Get_PyObject_ob_refcnt extends CApiUnaryBuiltinNode { @Specialization - static Object get(PythonAbstractObjectNativeWrapper wrapper) { + static long get(long pointer) { /* * We are allocating native object stubs for each wrapper. Therefore, reference counting * should only be done on the native side. However, we allow access for debugging * purposes. */ if (PythonContext.DEBUG_CAPI) { - return wrapper.getRefCount(); + return CApiTransitions.readNativeRefCount(HandlePointerConverter.pointerToStub(pointer)); } throw CompilerDirectives.shouldNotReachHere(); } @@ -676,11 +666,10 @@ static Object doStop(PSlice object, abstract static class GraalPyPrivate_Get_PyUnicodeObject_data extends CApiUnaryBuiltinNode { @Specialization - static Object get(PString object, + static long get(PString object, @Bind Node inliningTarget, @Cached TruffleString.GetCodeRangeNode getCodeRangeNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, - @Cached CStructAccess.AllocateNode allocateNode, @Cached CStructAccess.WriteTruffleStringNode writeTruffleStringNode, @Cached HiddenAttr.ReadNode readAttrNode, @Cached HiddenAttr.WriteNode writeAttrNode) { @@ -710,7 +699,7 @@ static Object get(PString object, } string = switchEncodingNode.execute(string, encoding); int byteLength = string.byteLength(encoding); - Object ptr = allocateNode.alloc(byteLength + /* null terminator */ charSize); + long ptr = calloc(byteLength + /* null terminator */ charSize); writeTruffleStringNode.write(ptr, string, encoding); /* * Set native data, so we can just return the pointer the next time. diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextStructSeqBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextStructSeqBuiltins.java index 078dc71c25..c400726e9a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextStructSeqBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextStructSeqBuiltins.java @@ -49,6 +49,8 @@ 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.PyTypeObjectTransfer; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.readPtrArrayElement; import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY; import java.util.ArrayList; @@ -62,7 +64,6 @@ 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.cext.capi.CExtNodes.FromCharPointerNode; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.tuple.PTuple; @@ -87,8 +88,6 @@ 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.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.object.DynamicObject; import com.oracle.truffle.api.strings.TruffleString; @@ -100,10 +99,7 @@ abstract static class GraalPyPrivate_StructSequence_InitType2 extends CApiTernar @Specialization @TruffleBoundary - static int doGeneric(PythonAbstractClass klass, Object fields, int nInSequence, - @CachedLibrary(limit = "3") InteropLibrary lib, - @Cached CStructAccess.ReadPointerNode readNode, - @Cached FromCharPointerNode fromCharPtr) { + static int doGeneric(PythonAbstractClass klass, long fields, int nInSequence) { ArrayList names = new ArrayList<>(); ArrayList docs = new ArrayList<>(); @@ -112,13 +108,14 @@ static int doGeneric(PythonAbstractClass klass, Object fields, int nInSequence, while (true) { - Object name = readNode.readArrayElement(fields, pos * 2); - if ((name instanceof Long && (long) name == 0) || lib.isNull(name)) { + long name = readPtrArrayElement(fields, pos * 2L); + if (name == NULLPTR) { break; } - Object doc = readNode.readArrayElement(fields, pos * 2 + 1); - names.add(fromCharPtr.execute(name)); - docs.add(lib.isNull(doc) ? null : fromCharPtr.execute(doc)); + long doc = readPtrArrayElement(fields, pos * 2L + 1); + // CPython also directly references name and doc string pointers without copying. + names.add(FromCharPointerNode.executeUncached(name, false)); + docs.add(doc == NULLPTR ? null : FromCharPointerNode.executeUncached(doc, false)); pos++; } @@ -126,7 +123,7 @@ static int doGeneric(PythonAbstractClass klass, Object fields, int nInSequence, TruffleString[] fieldDocs = docs.toArray(TruffleString[]::new); StructSequence.Descriptor d = new StructSequence.Descriptor(nInSequence, fieldNames, fieldDocs); - StructSequence.initType(PythonContext.get(readNode), klass, d); + StructSequence.initType(PythonContext.get(null), klass, d); return 0; } } @@ -136,7 +133,7 @@ abstract static class GraalPyPrivate_StructSequence_NewType extends CApiQuaterna @Specialization @TruffleBoundary - Object doGeneric(TruffleString typeName, TruffleString typeDoc, Object fields, int nInSequence, + Object doGeneric(TruffleString typeName, TruffleString typeDoc, long fields, int nInSequence, @Cached GraalPyPrivate_StructSequence_InitType2 initNode, @Cached ReadAttributeFromModuleNode readTypeBuiltinNode, @Cached DynamicObject.SetShapeFlagsNode setShapeFlagsNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSysBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSysBuiltins.java index af57cf456c..2f17227632 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSysBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSysBuiltins.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 @@ -43,21 +43,22 @@ import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored; import static com.oracle.graal.python.builtins.modules.io.IONodes.T_WRITE; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtr; 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.PyObjectBorrowed; -import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.VA_LIST_PTR; import static com.oracle.graal.python.nodes.BuiltinNames.T_STDERR; import static com.oracle.graal.python.nodes.BuiltinNames.T_STDOUT; import static com.oracle.graal.python.nodes.BuiltinNames.T_SYS; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; -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.PythonCextBuiltins.PromoteBorrowedValue; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.UnicodeFromFormatNode; +import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.lib.PyObjectLookupAttr; @@ -87,7 +88,7 @@ Object getObject(TruffleString name, PythonModule sys = getCore().lookupBuiltinModule(T_SYS); Object value = lookupNode.execute(null, inliningTarget, sys, name); if (value == PNone.NO_VALUE) { - return getNativeNull(); + return NATIVE_NULL; } Object promotedValue = promoteNode.execute(inliningTarget, value); if (promotedValue != null) { @@ -98,7 +99,7 @@ Object getObject(TruffleString name, } catch (PException e) { // PySys_GetObject delegates to PyDict_GetItem // which suppresses all exceptions for historical reasons - return getNativeNull(); + return NATIVE_NULL; } } } @@ -115,33 +116,21 @@ private static Object selectOut(int fd) { return file; } - @CApiBuiltin(ret = Int, args = {Int, ConstCharPtrAsTruffleString}, call = Ignored) - abstract static class GraalPyPrivate_Sys_WriteStd extends CApiBinaryBuiltinNode { - @Specialization - @TruffleBoundary - static Object doGeneric(int fd, TruffleString msg) { - try { - PyObjectCallMethodObjArgs.executeUncached(selectOut(fd), T_WRITE, msg); - return 0; - } catch (PException e) { - return -1; - } - } + /** similar to {@code sys_pyfile_write} */ + @CApiBuiltin(ret = Int, args = {Int, ConstCharPtr}, call = Ignored, acquireGil = false) + @TruffleBoundary + public static int GraalPyPrivate_Sys_WriteStd(int fd, long msgPtr) { + TruffleString msg = FromCharPointerNode.executeUncached(msgPtr, false); + PyObjectCallMethodObjArgs.executeUncached(selectOut(fd), T_WRITE, msg); + return 0; } - @CApiBuiltin(ret = Int, args = {Int, ConstCharPtrAsTruffleString, VA_LIST_PTR}, call = Ignored) - abstract static class GraalPyPrivate_Sys_FormatStd extends CApiTernaryBuiltinNode { - @Specialization - @TruffleBoundary - static Object doGeneric(int fd, TruffleString format, Object vaList) { - try { - Object msg = UnicodeFromFormatNode.executeUncached(format, vaList); - PyObjectCallMethodObjArgs.executeUncached(selectOut(fd), T_WRITE, msg); - return 0; - } catch (PException e) { - // do not propagate any exception to native - return -1; - } - } + /** similar to {@code sys_pyfile_write_unicode} */ + @CApiBuiltin(ret = Int, args = {Int, PyObject}, call = Ignored, acquireGil = false) + @TruffleBoundary + public static int GraalPyPrivate_Sys_PyFileWriteUnicode(int fd, long unicodePtr) { + Object msg = NativeToPythonInternalNode.executeUncached(unicodePtr, false); + PyObjectCallMethodObjArgs.executeUncached(selectOut(fd), T_WRITE, msg); + return 0; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTracebackBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTracebackBuiltins.java index 6f13abea8e..ce8ffd0192 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTracebackBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTracebackBuiltins.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,12 +45,12 @@ 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.PyFrameObject; 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.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.PythonCextCodeBuiltins.PyCode_NewEmpty; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes; import com.oracle.graal.python.builtins.objects.exception.ExceptionNodes; @@ -73,10 +73,10 @@ public final class PythonCextTracebackBuiltins { abstract static class _PyTraceback_Add extends CApiTernaryBuiltinNode { @Specialization static Object tbHere(TruffleString funcname, TruffleString filename, int lineno, - @Cached PyCode_NewEmpty newCode, @Cached PyTraceBack_Here pyTraceBackHereNode, @Bind PythonLanguage language) { - PFrame frame = PFactory.createPFrame(language, null, newCode.execute(filename, funcname, lineno), PFactory.createDict(language), PFactory.createDict(language)); + PFrame frame = PFactory.createPFrame(language, NULLPTR, PythonCextCodeBuiltins.createCodeNewEmpty(filename, funcname, lineno), PFactory.createDict(language), + PFactory.createDict(language)); pyTraceBackHereNode.execute(frame); return PNone.NONE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java index d0c94226f2..fc3bbebe05 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.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 @@ -48,6 +48,8 @@ import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectPtr; 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.runtime.nativeaccess.NativeMemory.callocPtrArray; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; @@ -58,7 +60,6 @@ import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.PromoteBorrowedValue; 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.EnsureCapacityNode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetItemNode; @@ -93,8 +94,7 @@ abstract static class PyTuple_New extends CApiUnaryBuiltinNode { static PTuple doGeneric(long longSize, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached PRaiseNode raiseNode, - @Cached CStructAccess.AllocateNode alloc) { + @Cached PRaiseNode raiseNode) { int size = (int) longSize; if (longSize != size) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.MemoryError); @@ -103,7 +103,7 @@ static PTuple doGeneric(long longSize, * Already allocate the tuple with native memory, since it has to be populated from the * native side */ - Object mem = alloc.alloc((longSize + 1) * CStructAccess.POINTER_SIZE); + long mem = callocPtrArray(longSize + 1); NativeObjectSequenceStorage storage = NativeObjectSequenceStorage.create(mem, size, size, true); return PFactory.createTuple(language, storage); } @@ -153,7 +153,7 @@ static Object doNative(PythonAbstractNativeObject tuple, long key, return promotedValue; } if (result == null) { - return getNativeNull(inliningTarget); + return NATIVE_NULL; } return result; } @@ -175,7 +175,7 @@ private static int checkIndex(Node inliningTarget, long key, SequenceStorage seq @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Direct) abstract static class PyTuple_Size extends CApiUnaryBuiltinNode { @Specialization - public static int size(Object tuple, + public static long size(Object tuple, @Bind Node inliningTarget, @Cached PyTupleSizeNode pyTupleSizeNode) { return pyTupleSizeNode.execute(inliningTarget, tuple); @@ -215,7 +215,7 @@ private static Object doGetSlice(SequenceStorage storage, Node inliningTarget, O @CApiBuiltin(ret = PyObjectPtr, args = {PyObject, Py_ssize_t, PyObjectPtr}, call = Ignored) abstract static class GraalPyPrivate_Tuple_Resize extends CApiTernaryBuiltinNode { @Specialization - public static Object size(PTuple tuple, long size, Object obItemsPtr, + public static long size(PTuple tuple, long size, long obItemsPtr, @Bind Node inliningTarget, @Cached EnsureCapacityNode ensureCapacityNode, @Cached SetLenNode setLenNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java index 0396efc05f..cf0be21c8f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTypeBuiltins.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,32 +42,33 @@ import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Ignored; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.ConstCharPtr; 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.Pointer; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyBufferProcs; 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.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.PyTypeObject; +import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyTypeObjectRawPointer; 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.CExtContext.METH_CLASS; import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_name; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.nodes.HiddenAttr.AS_BUFFER; import static com.oracle.graal.python.nodes.HiddenAttr.METHOD_DEF_PTR; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; 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.modules.cext.PythonCextBuiltins.CApi7BuiltinNode; -import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi8BuiltinNode; 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.CApiCallPath; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode; -import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.PyObjectSetAttrNode; 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; @@ -79,9 +80,16 @@ import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.GetterRoot; import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper; import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.SetterRoot; +import com.oracle.graal.python.builtins.objects.cext.capi.MethodDescriptorWrapper; +import com.oracle.graal.python.builtins.objects.cext.capi.WrapperDescriptorRootNodesGen; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor; -import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers.CArrayWrapper; -import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.EnsureExecutableNode; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions; +import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.CharPtrToPythonNode; +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.NativeToPythonNode; +import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes; +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.cext.structs.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; @@ -90,7 +98,7 @@ 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.getsetdescriptor.GetSetDescriptor; -import com.oracle.graal.python.builtins.objects.object.PythonObject; +import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -98,31 +106,31 @@ import com.oracle.graal.python.lib.PyDictGetItem; import com.oracle.graal.python.lib.PyDictSetDefault; import com.oracle.graal.python.lib.PyDictSetItem; +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.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; import com.oracle.graal.python.nodes.object.SetDictNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.PythonContext; +import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage; 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.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.GenerateCached; -import com.oracle.truffle.api.dsl.GenerateInline; -import com.oracle.truffle.api.dsl.ImportStatic; 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.EncapsulatingNodeReference; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.object.DynamicObject; @@ -182,7 +190,7 @@ Object doGeneric(Object type, Object name, return value; } } - return getNativeNull(); + return NATIVE_NULL; } } @@ -238,7 +246,7 @@ static Object doIt(PythonAbstractClass object, // Reload slots from native, which also invalidates cached slot lookups clazz.setTpSlots(TpSlots.fromNative(clazz, context)); } else if (object instanceof PythonManagedClass clazz) { - Object field = readObjectNode.read(clazz.getClassNativeWrapper().getNativePointer(), CFields.PyTypeObject__tp_dict); + Object field = readObjectNode.read(clazz.getNativePointer(), CFields.PyTypeObject__tp_dict); if (field instanceof PDict dict && dict != getDictIfExists.execute(clazz)) { setDict.execute(inliningTarget, object, dict); } @@ -256,180 +264,190 @@ abstract static class GraalPyPrivate_Trace_Type extends CApiUnaryBuiltinNode { @Specialization @TruffleBoundary - int trace(Object ptr) { + int trace(long ptr) { LOGGER.fine(() -> PythonUtils.formatJString("Initializing native type %s (ptr = %s)", - CStructAccess.ReadCharPtrNode.getUncached().read(ptr, PyTypeObject__tp_name), - CApiContext.asHex(CApiContext.asPointer(ptr, InteropLibrary.getUncached())))); + CStructAccess.ReadCharPtrNode.executeUncached(ptr, PyTypeObject__tp_name), + CApiContext.asHex(ptr))); return 0; } } - @GenerateInline - @GenerateCached(false) - @ImportStatic(CExtContext.class) - abstract static class NewClassMethodNode extends Node { - - abstract Object execute(Node inliningTarget, Object methodDefPtr, TruffleString name, Object methObj, Object flags, Object wrapper, Object type, Object doc); - - @Specialization(guards = "isClassOrStaticMethod(flags)") - static Object classOrStatic(Node inliningTarget, Object methodDefPtr, TruffleString name, Object methObj, int flags, int wrapper, Object type, Object doc, - @Bind PythonLanguage language, - @Exclusive @Cached HiddenAttr.WriteNode writeHiddenAttrNode, - @Cached(inline = false) WriteAttributeToPythonObjectNode writeAttrNode) { - PythonAbstractObject func = PExternalFunctionWrapper.createWrapperFunction(name, methObj, type, flags, wrapper, language); - writeHiddenAttrNode.execute(inliningTarget, func, METHOD_DEF_PTR, methodDefPtr); - PythonObject function; - if ((flags & METH_CLASS) != 0) { - function = PFactory.createClassmethodFromCallableObj(language, func); - } else { - function = PFactory.createStaticmethodFromCallableObj(language, func); + /** + * Similar to {@code type_add_method}, this will either create a + * {@link PythonBuiltinClassType#PBuiltinClassMethod}, + * {@link PythonBuiltinClassType#PStaticmethod}, or + * {@link PythonBuiltinClassType#PBuiltinFunction}. + * + * @param language The Python language instance. + * @param methodDefPtr The native + * {@link com.oracle.graal.python.builtins.objects.cext.structs.CStructs#PyMethodDef} + * struct. + * @param name The name of the attribute. + * @param methPtr The native function pointer ({@code PyCFunction}). + * @param flags The method flags (i.e. {@code methodDef->flags}). + * @param type The enclosing type. + * @param doc The doc string (usually a + * @return + */ + private static PythonBuiltinObject typeAddMethod(PythonLanguage language, long methodDefPtr, TruffleString name, long methPtr, int flags, Object type, Object doc) { + assert doc == PNone.NO_VALUE || doc instanceof TruffleString; + PBuiltinFunction func = MethodDescriptorWrapper.createWrapperFunction(language, name, methPtr, type, flags); + if (CExtContext.isMethClass(flags)) { + if (CExtContext.isMethStatic(flags)) { + assert func == null; + throw PRaiseNode.raiseStatic(EncapsulatingNodeReference.getCurrent().get(), PythonBuiltinClassType.ValueError, ErrorMessages.METHOD_CANNOT_BE_BOTH_CLASS_AND_STATIC); } - writeAttrNode.execute(function, T___NAME__, name); - writeAttrNode.execute(function, T___DOC__, doc); - return function; - } - - @Specialization(guards = "!isClassOrStaticMethod(flags)") - static Object doNativeCallable(Node inliningTarget, Object methodDefPtr, TruffleString name, Object methObj, int flags, int wrapper, Object type, Object doc, - @Bind PythonLanguage language, - @Cached PyObjectSetAttrNode setattr, - @Exclusive @Cached HiddenAttr.WriteNode writeNode) { - PythonAbstractObject func = PExternalFunctionWrapper.createWrapperFunction(name, methObj, type, flags, wrapper, language); - setattr.execute(inliningTarget, func, T___NAME__, name); - setattr.execute(inliningTarget, func, T___DOC__, doc); - writeNode.execute(inliningTarget, func, METHOD_DEF_PTR, methodDefPtr); - return func; + assert func != null; + return PFactory.createClassmethodFromCallableObj(language, func); + } else if (CExtContext.isMethStatic(flags)) { + return PFactory.createStaticmethodFromCallableObj(language, func); } + WriteAttributeToPythonObjectNode.executeUncached(func, T___NAME__, name); + WriteAttributeToPythonObjectNode.executeUncached(func, T___DOC__, doc); + HiddenAttr.WriteLongNode.executeUncached(func, METHOD_DEF_PTR, methodDefPtr); + return func; } - @CApiBuiltin(ret = Int, args = {Pointer, PyTypeObject, PyObject, ConstCharPtrAsTruffleString, Pointer, Int, Int, ConstCharPtrAsTruffleString}, call = Ignored) - abstract static class GraalPyPrivate_Type_AddFunctionToType extends CApi8BuiltinNode { - - @Specialization - static int classMethod(Object methodDefPtr, Object type, Object dict, TruffleString name, Object cfunc, int flags, int wrapper, Object doc, - @Bind Node inliningTarget, - @Cached NewClassMethodNode newClassMethodNode, - @Cached PyDictSetDefault setDefault) { - Object func = newClassMethodNode.execute(inliningTarget, methodDefPtr, name, cfunc, flags, wrapper, type, doc); - setDefault.execute(null, inliningTarget, dict, name, func); + @CApiBuiltin(ret = Int, args = {Pointer, PyTypeObject, PyObject, ConstCharPtrAsTruffleString, Pointer, Int, ConstCharPtrAsTruffleString}, call = Ignored) + public static int GraalPyPrivate_Type_AddFunctionToType(long methodDefPtr, long typeRaw, long dictRaw, long nameRaw, long cfunc, int flags, long docRaw) { + CompilerAsserts.neverPartOfCompilation(); + try { + Object type = NativeToPythonClassInternalNode.executeUncached(typeRaw); + Object dict = NativeToPythonInternalNode.executeUncached(dictRaw, false); + TruffleString name = (TruffleString) CharPtrToPythonNode.getUncached().execute(nameRaw); + Object doc = CharPtrToPythonNode.getUncached().execute(docRaw); + assert doc == PNone.NO_VALUE || doc instanceof TruffleString; + Object func = typeAddMethod(PythonLanguage.get(null), methodDefPtr, name, cfunc, flags, type, doc); + PyDictSetDefault.executeUncached(dict, name, func); return 0; + } catch (PException t) { + TransformExceptionToNativeNode.executeUncached(t); + return -1; } } @CApiBuiltin(ret = Int, args = {PyTypeObject}, call = Ignored) - abstract static class GraalPyPrivate_Type_AddOperators extends CApiUnaryBuiltinNode { - - @Specialization - @TruffleBoundary - static int addOperators(PythonAbstractNativeObject type) { - TpSlots.addOperatorsToNative(type); - return 0; + public static int GraalPyPrivate_Type_AddOperators(long type) { + CompilerAsserts.neverPartOfCompilation(); + try { + Object clazz = NativeToPythonClassInternalNode.executeUncached(type); + if (clazz instanceof PythonAbstractNativeObject nativeClass) { + TpSlots.addOperatorsToNative(nativeClass); + return 0; + } + } catch (PException t) { + TransformExceptionToNativeNode.executeUncached(t); } + return -1; } - @CApiBuiltin(ret = Int, args = {PyTypeObject, PyObject, ConstCharPtrAsTruffleString, Int, Py_ssize_t, Int, ConstCharPtrAsTruffleString}, call = CApiCallPath.Ignored) - public abstract static class GraalPyPrivate_Type_AddMember extends CApi7BuiltinNode { - - @Specialization - @TruffleBoundary - public static int addMember(Object clazz, PDict tpDict, TruffleString memberName, int memberType, long offset, int canSet, Object memberDoc) { - // note: 'doc' may be NULL; in this case, we would store 'None' - PythonLanguage language = PythonLanguage.get(null); - PBuiltinFunction getterObject = ReadMemberNode.createBuiltinFunction(language, clazz, memberName, memberType, (int) offset); - - Object setterObject = null; - if (canSet != 0) { - setterObject = WriteMemberNode.createBuiltinFunction(language, clazz, memberName, memberType, (int) offset); - } + @CApiBuiltin(ret = Int, args = {PyTypeObjectRawPointer, PyObjectRawPointer, ConstCharPtr, Int, Py_ssize_t, Int, ConstCharPtr}, call = CApiCallPath.Ignored) + @TruffleBoundary + public static int GraalPyPrivate_Type_AddMember(long clazzPtr, long tpDictPtr, long memberNamePtr, int memberType, long offset, int canSet, long memberDocPtr) { + Object clazz = NativeToPythonClassInternalNode.executeUncached(clazzPtr); + PDict tpDict = (PDict) NativeToPythonNode.executeRawUncached(tpDictPtr); + TruffleString memberName = (TruffleString) CharPtrToPythonNode.getUncached().execute(memberNamePtr); + Object memberDoc = memberDocPtr == NULLPTR ? PNone.NO_VALUE : CharPtrToPythonNode.getUncached().execute(memberDocPtr); + return addMember(clazz, tpDict, memberName, memberType, offset, canSet, memberDoc); + } - // create member descriptor - GetSetDescriptor memberDescriptor = PFactory.createMemberDescriptor(language, getterObject, setterObject, memberName, clazz); - WriteAttributeToPythonObjectNode.getUncached().execute(memberDescriptor, SpecialAttributeNames.T___DOC__, memberDoc); + @TruffleBoundary + public static int addMember(Object clazz, PDict tpDict, TruffleString memberName, int memberType, long offset, int canSet, Object memberDoc) { + // note: 'doc' may be NULL; in this case, we would store 'None' + PythonLanguage language = PythonLanguage.get(null); + PBuiltinFunction getterObject = ReadMemberNode.createBuiltinFunction(language, clazz, memberName, memberType, (int) offset); - // add member descriptor to tp_dict - PyDictSetDefault.executeUncached(tpDict, memberName, memberDescriptor); - return 0; + Object setterObject = null; + if (canSet != 0) { + setterObject = WriteMemberNode.createBuiltinFunction(language, clazz, memberName, memberType, (int) offset); } - } - @GenerateInline - @GenerateCached(false) - abstract static class CreateGetSetNode extends Node { + // create member descriptor + GetSetDescriptor memberDescriptor = PFactory.createMemberDescriptor(language, getterObject, setterObject, memberName, clazz); + WriteAttributeToPythonObjectNode.getUncached().execute(memberDescriptor, SpecialAttributeNames.T___DOC__, memberDoc); - abstract GetSetDescriptor execute(Node inliningTarget, TruffleString name, Object cls, Object getter, Object setter, Object doc, Object closure); + // add member descriptor to tp_dict + PyDictSetDefault.executeUncached(tpDict, memberName, memberDescriptor); + return 0; + } - @Specialization - @TruffleBoundary - static GetSetDescriptor createGetSet(Node inliningTarget, TruffleString name, Object cls, Object getter, Object setter, Object doc, Object closure, - @CachedLibrary(limit = "2") InteropLibrary interopLibrary) { - assert !(doc instanceof CArrayWrapper); - // note: 'doc' may be NULL; in this case, we would store 'None' - PBuiltinFunction get = null; - PythonLanguage language = PythonLanguage.get(inliningTarget); - if (!interopLibrary.isNull(getter)) { - RootCallTarget getterCT = getterCallTarget(name, language); - getter = EnsureExecutableNode.executeUncached(getter, PExternalFunctionWrapper.GETTER); - get = PFactory.createBuiltinFunction(language, name, cls, EMPTY_OBJECT_ARRAY, ExternalFunctionNodes.createKwDefaults(getter, closure), 0, getterCT); - } + @CApiBuiltin(ret = Int, args = {PyTypeObjectRawPointer, PyObjectRawPointer, ConstCharPtr, Pointer, Pointer, ConstCharPtr, Pointer}, call = Ignored) + static int GraalPyPrivate_Type_AddGetSet(long clsPtr, long dictPtr, long namePtr, long getter, long setter, long docPtr, long closure) { + Object cls = NativeToPythonClassInternalNode.executeUncached(clsPtr); + PDict dict = (PDict) NativeToPythonNode.executeRawUncached(dictPtr); + TruffleString name = (TruffleString) CharPtrToPythonNode.getUncached().execute(namePtr); + Object doc = docPtr == NULLPTR ? PNone.NO_VALUE : CharPtrToPythonNode.getUncached().execute(docPtr); + return addGetSet(cls, dict, name, getter, setter, doc, closure); + } - PBuiltinFunction set = null; - boolean hasSetter = !interopLibrary.isNull(setter); - if (hasSetter) { - RootCallTarget setterCT = setterCallTarget(name, language); - setter = EnsureExecutableNode.executeUncached(setter, PExternalFunctionWrapper.SETTER); - set = PFactory.createBuiltinFunction(language, name, cls, EMPTY_OBJECT_ARRAY, ExternalFunctionNodes.createKwDefaults(setter, closure), 0, setterCT); - } + public static int addGetSet(Object cls, PDict dict, TruffleString name, long getter, long setter, Object doc, long closure) { + GetSetDescriptor descr = createGetSet(name, cls, getter, setter, doc, closure); + PyDictSetDefault.executeUncached(dict, name, descr); + return 0; + } - // create get-set descriptor - GetSetDescriptor descriptor = PFactory.createGetSetDescriptor(language, get, set, name, cls, hasSetter); - WriteAttributeToPythonObjectNode.executeUncached(descriptor, T___DOC__, doc); - return descriptor; + @TruffleBoundary + public static GetSetDescriptor createGetSet(TruffleString name, Object cls, long getter, long setter, Object doc, long closure) { + // note: 'doc' may be NULL; in this case, we would store 'None' + PBuiltinFunction get = null; + PythonLanguage language = PythonLanguage.get(null); + if (getter != NULLPTR) { + RootCallTarget getterCT = getterCallTarget(name, language); + NativeFunctionPointer getterFun = CExtCommonNodes.bindFunctionPointer(getter, PExternalFunctionWrapper.GETTER); + get = PFactory.createBuiltinFunction(language, name, cls, EMPTY_OBJECT_ARRAY, ExternalFunctionNodes.createKwDefaults(getterFun, closure), 0, getterCT); } - @TruffleBoundary - private static RootCallTarget getterCallTarget(TruffleString name, PythonLanguage lang) { - Function rootNodeFunction = l -> new GetterRoot(l, name, PExternalFunctionWrapper.GETTER); - return lang.createCachedExternalFunWrapperCallTarget(rootNodeFunction, GetterRoot.class, PExternalFunctionWrapper.GETTER, name, true, false); + PBuiltinFunction set = null; + boolean hasSetter = setter != NULLPTR; + if (hasSetter) { + RootCallTarget setterCT = setterCallTarget(name, language); + NativeFunctionPointer setterFun = CExtCommonNodes.bindFunctionPointer(setter, PExternalFunctionWrapper.SETTER); + set = PFactory.createBuiltinFunction(language, name, cls, EMPTY_OBJECT_ARRAY, ExternalFunctionNodes.createKwDefaults(setterFun, closure), 0, setterCT); } - @TruffleBoundary - private static RootCallTarget setterCallTarget(TruffleString name, PythonLanguage lang) { - Function rootNodeFunction = l -> new SetterRoot(l, name, PExternalFunctionWrapper.SETTER); - return lang.createCachedExternalFunWrapperCallTarget(rootNodeFunction, SetterRoot.class, PExternalFunctionWrapper.SETTER, name, true, false); - } + GetSetDescriptor descriptor = PFactory.createGetSetDescriptor(language, get, set, name, cls, hasSetter); + WriteAttributeToPythonObjectNode.executeUncached(descriptor, T___DOC__, doc); + return descriptor; } - @CApiBuiltin(ret = Int, args = {PyTypeObject, PyObject, ConstCharPtrAsTruffleString, Pointer, Pointer, ConstCharPtrAsTruffleString, Pointer}, call = Ignored) - abstract static class GraalPyPrivate_Type_AddGetSet extends CApi7BuiltinNode { + @TruffleBoundary + private static RootCallTarget getterCallTarget(TruffleString name, PythonLanguage lang) { + Function rootNodeFunction = l -> WrapperDescriptorRootNodesGen.create(l, name, PExternalFunctionWrapper.GETTER); + return lang.createCachedExternalFunWrapperCallTarget(rootNodeFunction, GetterRoot.class, PExternalFunctionWrapper.GETTER, name, true, false); + } - @Specialization - static int doGeneric(Object cls, PDict dict, TruffleString name, Object getter, Object setter, Object doc, Object closure, - @Bind Node inliningTarget, - @Cached CreateGetSetNode createGetSetNode, - @Cached PyDictSetDefault setDefault) { - GetSetDescriptor descr = createGetSetNode.execute(inliningTarget, name, cls, getter, setter, doc, closure); - setDefault.execute(null, inliningTarget, dict, name, descr); - return 0; - } + @TruffleBoundary + private static RootCallTarget setterCallTarget(TruffleString name, PythonLanguage lang) { + Function rootNodeFunction = l -> WrapperDescriptorRootNodesGen.create(l, name, PExternalFunctionWrapper.SETTER); + return lang.createCachedExternalFunWrapperCallTarget(rootNodeFunction, SetterRoot.class, PExternalFunctionWrapper.SETTER, name, true, false); } @CApiBuiltin(ret = ArgDescriptor.Void, args = {PyTypeObject, PyBufferProcs}, call = Ignored) abstract static class GraalPyPrivate_Type_SetBufferProcs extends CApiBinaryBuiltinNode { @Specialization - static Object setBuiltinClassType(PythonBuiltinClassType clazz, Object bufferProcs, + static Object setBuiltinClassType(PythonBuiltinClassType clazz, long bufferProcs, @Bind Node inliningTarget, - @Shared @Cached HiddenAttr.WriteNode writeAttrNode) { + @Shared @Cached HiddenAttr.WriteLongNode writeAttrNode) { writeAttrNode.execute(inliningTarget, PythonContext.get(inliningTarget).lookupType(clazz), AS_BUFFER, bufferProcs); return PNone.NO_VALUE; } @Specialization(guards = "isPythonClass(object)") - static Object set(PythonAbstractObject object, Object bufferProcs, + static Object set(PythonAbstractObject object, long bufferProcs, @Bind Node inliningTarget, - @Shared @Cached HiddenAttr.WriteNode writeAttrNode) { + @Shared @Cached HiddenAttr.WriteLongNode writeAttrNode) { writeAttrNode.execute(inliningTarget, object, AS_BUFFER, bufferProcs); return PNone.NO_VALUE; } + + } + + @CApiBuiltin(ret = ArgDescriptor.Void, args = {Pointer, Int}, call = Ignored) + public static void GraalPyPrivate_Type_NotifyDealloc(long ptr, int typeLookupIdx) { + assert CStructAccess.readIntField(ptr, CFields.PyTypeObject__tp_version_tag) == typeLookupIdx; + if (typeLookupIdx != 0) { + CApiTransitions.nativeTypeLookupRemove(PythonContext.get(null).handleContext, ptr, typeLookupIdx); + } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java index 4d3e669ea0..4aeaa8643a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextUnicodeBuiltins.java @@ -68,8 +68,12 @@ 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.Py_ssize_t; -import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.VA_LIST_PTR; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor._PY_ERROR_HANDLER; +import static com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.getByteArray; +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.readByteArrayElement; import static com.oracle.graal.python.nodes.ErrorMessages.BAD_ARG_TYPE_FOR_BUILTIN_OP; import static com.oracle.graal.python.nodes.ErrorMessages.PRECISION_TOO_LARGE; import static com.oracle.graal.python.nodes.ErrorMessages.SEPARATOR_EXPECTED_STR_INSTANCE_P_FOUND; @@ -79,6 +83,7 @@ import static com.oracle.graal.python.nodes.StringLiterals.T_STRICT; import static com.oracle.graal.python.nodes.StringLiterals.T_UTF8; import static com.oracle.graal.python.nodes.util.CastToJavaIntLossyNode.castLong; +import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_16LE; import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_32LE; @@ -98,7 +103,6 @@ import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApi6BuiltinNode; 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.CApiCallPath; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiQuaternaryBuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiTernaryBuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode; @@ -106,11 +110,12 @@ import com.oracle.graal.python.builtins.objects.PNone; 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.capi.CExtNodes.UnicodeFromFormatNode; +import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode; import com.oracle.graal.python.builtins.objects.cext.capi.PySequenceArrayWrapper; import com.oracle.graal.python.builtins.objects.cext.capi.UnicodeObjectNodes.UnicodeAsWideCharNode; +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.common.CExtCommonNodes.EncodeNativeStringNode; -import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.GetByteArrayNode; import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.ReadUnicodeArrayNode; import com.oracle.graal.python.builtins.objects.cext.structs.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; @@ -135,6 +140,7 @@ import com.oracle.graal.python.lib.PyUnicodeFSDecoderNode; import com.oracle.graal.python.lib.PyUnicodeFromEncodedObject; import com.oracle.graal.python.lib.RichCmpOp; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PGuards; @@ -164,12 +170,10 @@ 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.interop.InteropException; -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.InlinedConditionProfile; import com.oracle.truffle.api.profiles.InlinedExactClassProfile; +import com.oracle.truffle.api.strings.InternalByteArray; import com.oracle.truffle.api.strings.TruffleString; import com.oracle.truffle.api.strings.TruffleString.Encoding; import com.oracle.truffle.api.strings.TruffleString.FromNativePointerNode; @@ -316,7 +320,7 @@ static Object withPString(PString str, @Bind Node inliningTarget, @Cached PyUnicodeCheckExactNode unicodeCheckExactNode) { if (!unicodeCheckExactNode.execute(inliningTarget, str)) { - return getNativeNull(inliningTarget); + return NATIVE_NULL; } str.intern(); @@ -334,7 +338,7 @@ Object nil(@SuppressWarnings("unused") Object obj) { * If it's a subclass, we don't really know what putting it in the interned dict might * do. */ - return getNativeNull(); + return NATIVE_NULL; } } @@ -457,27 +461,27 @@ static Object formatLong(Object val, int alt, int prec, int type, @ImportStatic(PythonCextUnicodeBuiltins.class) abstract static class PyUnicode_FindChar extends CApi5BuiltinNode { @Specialization(guards = {"isString(string) || isStringSubtype(inliningTarget, string, getClassNode, isSubtypeNode)", "direction > 0"}) - static Object find(Object string, Object c, long start, long end, @SuppressWarnings("unused") int direction, + static long find(Object string, Object c, long start, long end, @SuppressWarnings("unused") int direction, @SuppressWarnings("unused") @Bind Node inliningTarget, @Shared @Cached ChrNode chrNode, @Cached FindNode findNode, @SuppressWarnings("unused") @Shared @Cached GetClassNode getClassNode, @SuppressWarnings("unused") @Shared @Cached IsSubtypeNode isSubtypeNode) { - return findNode.execute(null, string, chrNode.execute(null, c), start, end); + return (Integer) findNode.execute(null, string, chrNode.execute(null, c), start, end); } @Specialization(guards = {"isString(string) || isStringSubtype(inliningTarget, string, getClassNode, isSubtypeNode)", "direction <= 0"}) - static Object find(Object string, Object c, long start, long end, @SuppressWarnings("unused") int direction, + static long find(Object string, Object c, long start, long end, @SuppressWarnings("unused") int direction, @SuppressWarnings("unused") @Bind Node inliningTarget, @Shared @Cached ChrNode chrNode, @Cached RFindNode rFindNode, @SuppressWarnings("unused") @Shared @Cached GetClassNode getClassNode, @SuppressWarnings("unused") @Shared @Cached IsSubtypeNode isSubtypeNode) { - return rFindNode.execute(null, string, chrNode.execute(null, c), start, end); + return (Integer) rFindNode.execute(null, string, chrNode.execute(null, c), start, end); } @Specialization(guards = {"!isTruffleString(string)", "!isStringSubtype(inliningTarget, string, getClassNode, isSubtypeNode)"}) - static Object find(Object string, @SuppressWarnings("unused") Object c, @SuppressWarnings("unused") Object start, @SuppressWarnings("unused") Object end, + static long find(Object string, @SuppressWarnings("unused") Object c, @SuppressWarnings("unused") Object start, @SuppressWarnings("unused") Object end, @SuppressWarnings("unused") Object direction, @SuppressWarnings("unused") @Shared @Cached GetClassNode getClassNode, @SuppressWarnings("unused") @Shared @Cached IsSubtypeNode isSubtypeNode, @@ -562,13 +566,48 @@ static Object compare(Object left, Object right, } } - @CApiBuiltin(ret = Int, args = {PyObjectAsTruffleString, ConstCharPtrAsTruffleString}, call = Direct) - abstract static class PyUnicode_CompareWithASCIIString extends CApiBinaryBuiltinNode { + private static final CApiTiming TIMING_PYUNICODE_COMPAREWITHASCIISTRING = CApiTiming.create(false, "PyUnicode_CompareWithASCIIString"); + + @CApiBuiltin(ret = Int, args = {PyObjectAsTruffleString, ConstCharPtr}, call = Direct) + public static int PyUnicode_CompareWithASCIIString(long uniPtr, long str) { + /* + * This method cannot throw Python exceptions and doesn't need a GIL since both, the unicode + * object and the ASCII string are assumed to be immutable. + */ + CApiTiming.enter(); + try { + Object uniObj = NativeToPythonInternalNode.executeUncached(uniPtr, false); + /* + * This unchecked cast is fine because CPython will also just assume that the first + * argument is a unicode object and will crash otherwise. + */ + TruffleString left = CastToTruffleStringNode.castKnownStringUncached(uniObj); + if (left.isCompatibleToUncached(Encoding.US_ASCII)) { + InternalByteArray internalByteArray = left.switchEncodingUncached(Encoding.US_ASCII).getInternalByteArrayUncached(Encoding.US_ASCII); - @Specialization - static int compare(TruffleString left, TruffleString right, - @Cached TruffleString.CompareIntsUTF32Node compare) { - return compare.execute(left, right); + int len1 = internalByteArray.getLength(); + + // len2 = strlen(str) + int len2 = 0; + while (readByteArrayElement(str, len2) != 0) { + len2++; + } + + int len = Math.min(len1, len2); + for (int i = 0; i < len; i++) { + int cmp = (internalByteArray.get(i) & 0xFF) - (NativeMemory.readByteArrayElement(str, i) & 0xFF); + if (cmp != 0) { + return cmp < 0 ? -1 : 1; + } + } + return Integer.compare(len1, len2); + } + + TruffleString asciiString = FromCharPointerNode.executeUncached(str, false); + assert asciiString.isCompatibleToUncached(Encoding.US_ASCII); + return left.compareIntsUTF32Uncached(asciiString); + } finally { + CApiTiming.exit(TIMING_PYUNICODE_COMPAREWITHASCIISTRING); } } @@ -602,7 +641,7 @@ static Object compare(Object left, Object right, @ImportStatic(PythonCextUnicodeBuiltins.class) abstract static class PyUnicode_Tailmatch extends CApi5BuiltinNode { @Specialization(guards = {"isAnyString(inliningTarget, string, getClassNode, isSubtypeNode)", "isAnyString(inliningTarget, substring, getClassNode, isSubtypeNode)", "direction > 0"}) - static int tailmatch(Object string, Object substring, long start, long end, @SuppressWarnings("unused") int direction, + static long tailmatch(Object string, Object substring, long start, long end, @SuppressWarnings("unused") int direction, @Bind Node inliningTarget, @Shared @Cached PyObjectLookupAttr lookupAttrNode, @Shared @Cached PySliceNew sliceNode, @@ -616,7 +655,7 @@ static int tailmatch(Object string, Object substring, long start, long end, @Sup } @Specialization(guards = {"isAnyString(inliningTarget, string, getClassNode, isSubtypeNode)", "isAnyString(inliningTarget, substring, getClassNode, isSubtypeNode)", "direction <= 0"}) - static int tailmatch(Object string, Object substring, long start, long end, @SuppressWarnings("unused") int direction, + static long tailmatch(Object string, Object substring, long start, long end, @SuppressWarnings("unused") int direction, @Bind Node inliningTarget, @Shared @Cached PyObjectLookupAttr lookupAttrNode, @Shared @Cached PySliceNew sliceNode, @@ -631,7 +670,7 @@ static int tailmatch(Object string, Object substring, long start, long end, @Sup @SuppressWarnings("unused") @Specialization(guards = {"!isAnyString(inliningTarget, string, getClassNode, isSubtypeNode) || !isAnyString(inliningTarget, substring, getClassNode, isSubtypeNode)"}) - static Object find(Object string, Object substring, Object start, Object end, Object direction, + static long find(Object string, Object substring, Object start, Object end, Object direction, @Shared @Cached GetClassNode getClassNode, @Shared @Cached IsSubtypeNode isSubtypeNode, @Bind Node inliningTarget) { @@ -690,7 +729,7 @@ static Object replace(Object s, Object substr, Object replstr, long count, @SuppressWarnings("unused") @Bind Node inliningTarget, @SuppressWarnings("unused") @Shared @Cached GetClassNode getClassNode, @SuppressWarnings("unused") @Shared @Cached IsSubtypeNode isSubtypeNode) { - return getNativeNull(inliningTarget); + return NATIVE_NULL; } } @@ -699,7 +738,7 @@ static Object replace(Object s, Object substr, Object replstr, long count, @ImportStatic(PythonCextUnicodeBuiltins.class) abstract static class _PyUnicode_JoinArray extends CApiTernaryBuiltinNode { @Specialization - static Object join(Object separatorObj, Object itemsObj, long seqlenlong, + static Object join(Object separatorObj, long itemsObj, long seqlenlong, @Bind Node inliningTarget, @Cached CStructAccess.ReadObjectNode readNode, @Cached TruffleStringBuilder.AppendStringNode appendStringNode, @@ -790,7 +829,7 @@ static int doGeneric(Object type, long lindex, @CApiBuiltin(ret = PyObjectTransfer, args = {Pointer, Py_ssize_t, Int, Int}, call = Ignored) abstract static class GraalPyPrivate_Unicode_New extends CApiQuaternaryBuiltinNode { @Specialization - static Object doGeneric(Object ptr, long elements, int charSize, int isAscii, + static Object doGeneric(long ptr, long elements, int charSize, int isAscii, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached HiddenAttr.WriteNode writeNode, @@ -819,7 +858,7 @@ private static TruffleString.CompactionLevel compactionLevelFromKind(Node inlini } @Specialization - static Object doNative(Object ptr, long byteLength, int kind, + static Object doNative(long ptr, long byteLength, int kind, @Bind Node inliningTarget, @Cached FromNativePointerWithCompactionUTF32Node fromNativePointerNode, @Cached PRaiseNode raiseNode) { @@ -857,7 +896,7 @@ private static Encoding encodingFromKind(Node inliningTarget, int kind, PRaiseNo } @Specialization - static Object doNative(Object ptr, long byteLength, int kind, + static Object doNative(long ptr, long byteLength, int kind, @Bind Node inliningTarget, @Cached FromNativePointerNode fromNativePointerNode, @Cached SwitchEncodingNode switchEncodingNode, @@ -908,19 +947,16 @@ static Object split(Object string, Object sep, Object maxsplit, @CApiBuiltin(ret = PyObjectTransfer, args = {Pointer, Py_ssize_t, ConstCharPtrAsTruffleString, Int}, call = Ignored) abstract static class GraalPyPrivate_Unicode_DecodeUTF8Stateful extends CApiQuaternaryBuiltinNode { @Specialization - static Object doUtf8Decode(Object cByteArray, long size, TruffleString errors, int reportConsumed, + static Object doUtf8Decode(long cByteArray, long size, TruffleString errors, int reportConsumed, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached GetByteArrayNode getByteArrayNode, @Cached CodecsModuleBuiltins.CodecsDecodeNode decode, @Cached PRaiseNode raiseNode) { try { - PBytes bytes = PFactory.createBytes(language, getByteArrayNode.execute(inliningTarget, cByteArray, size)); + PBytes bytes = PFactory.createBytes(language, getByteArray(cByteArray, size)); return decode.call(null, bytes, T_UTF8, errors, reportConsumed == 0); } catch (OverflowException e) { throw raiseNode.raise(inliningTarget, PythonErrorType.SystemError, ErrorMessages.INPUT_TOO_LONG); - } catch (InteropException e) { - throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.M, e); } } } @@ -929,14 +965,13 @@ static Object doUtf8Decode(Object cByteArray, long size, TruffleString errors, i abstract static class GraalPyPrivate_Unicode_DecodeUTF16Stateful extends CApi5BuiltinNode { @Specialization - static Object decode(Object cByteArray, long size, TruffleString errors, int byteorder, int reportConsumed, + static Object decode(long cByteArray, long size, TruffleString errors, int byteorder, int reportConsumed, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached GetByteArrayNode getByteArrayNode, @Cached CodecsModuleBuiltins.CodecsDecodeNode decode, @Cached PRaiseNode raiseNode) { try { - PBytes bytes = PFactory.createBytes(language, getByteArrayNode.execute(inliningTarget, cByteArray, size)); + PBytes bytes = PFactory.createBytes(language, getByteArray(cByteArray, size)); TruffleString encoding; if (byteorder == 0) { encoding = T_UTF_16; @@ -948,8 +983,6 @@ static Object decode(Object cByteArray, long size, TruffleString errors, int byt return decode.call(null, bytes, encoding, errors, reportConsumed == 0); } catch (OverflowException e) { throw raiseNode.raise(inliningTarget, PythonErrorType.SystemError, ErrorMessages.INPUT_TOO_LONG); - } catch (InteropException e) { - throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.M, e); } } } @@ -958,14 +991,13 @@ static Object decode(Object cByteArray, long size, TruffleString errors, int byt abstract static class GraalPyPrivate_Unicode_DecodeUTF32Stateful extends CApi5BuiltinNode { @Specialization - static Object decode(Object cByteArray, long size, TruffleString errors, int byteorder, int reportConsumed, + static Object decode(long cByteArray, long size, TruffleString errors, int byteorder, int reportConsumed, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached GetByteArrayNode getByteArrayNode, @Cached CodecsModuleBuiltins.CodecsDecodeNode decode, @Cached PRaiseNode raiseNode) { try { - PBytes bytes = PFactory.createBytes(language, getByteArrayNode.execute(inliningTarget, cByteArray, size)); + PBytes bytes = PFactory.createBytes(language, getByteArray(cByteArray, size)); TruffleString encoding; if (byteorder == 0) { encoding = T_UTF_32; @@ -977,8 +1009,6 @@ static Object decode(Object cByteArray, long size, TruffleString errors, int byt return decode.call(null, bytes, encoding, errors, reportConsumed == 0); } catch (OverflowException e) { throw raiseNode.raise(inliningTarget, PythonErrorType.SystemError, ErrorMessages.INPUT_TOO_LONG); - } catch (InteropException e) { - throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.M, e); } } } @@ -1021,7 +1051,7 @@ static Object encode(Object s, Object errors, @CApiBuiltin(ret = PyObjectTransfer, args = {CONST_WCHAR_PTR, Py_ssize_t}, call = Direct) abstract static class PyUnicode_FromWideChar extends CApiBinaryBuiltinNode { @Specialization - Object doInt(Object arr, long size, + Object doInt(long arr, long size, @Bind Node inliningTarget, @Cached ReadUnicodeArrayNode readArray, @Cached TruffleString.FromIntArrayUTF32Node fromArray) { @@ -1089,12 +1119,10 @@ public static _PyUnicode_AsUTF8String create() { abstract static class GraalPyPrivate_Unicode_AsUTF8AndSize extends CApiBinaryBuiltinNode { @Specialization - static Object doUnicode(PString s, Object sizePtr, + static long doUnicode(PString s, long sizePtr, @Bind Node inliningTarget, - @CachedLibrary(limit = "2") InteropLibrary lib, @Cached InlinedConditionProfile hasSizeProfile, @Cached InlinedConditionProfile hasUtf8Profile, - @Cached CStructAccess.WriteLongNode writeLongNode, @Cached _PyUnicode_AsUTF8String asUTF8String, @Cached HiddenAttr.ReadNode readAttrNode, @Cached HiddenAttr.WriteNode writeAttrNode) { @@ -1103,15 +1131,15 @@ static Object doUnicode(PString s, Object sizePtr, utf8bytes = (PBytes) asUTF8String.execute(s, T_STRICT); s.setUtf8Bytes(inliningTarget, writeAttrNode, utf8bytes); } - if (hasSizeProfile.profile(inliningTarget, !lib.isNull(sizePtr))) { - writeLongNode.write(sizePtr, utf8bytes.getSequenceStorage().length()); + if (hasSizeProfile.profile(inliningTarget, sizePtr != NULLPTR)) { + NativeMemory.writeLong(sizePtr, utf8bytes.getSequenceStorage().length()); } return PySequenceArrayWrapper.ensureNativeSequence(utf8bytes); } @Fallback @SuppressWarnings("unused") - static Object doError(Object s, Object sizePtr, + static long doError(Object s, Object sizePtr, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, TypeError, BAD_ARG_TYPE_FOR_BUILTIN_OP); } @@ -1122,17 +1150,14 @@ abstract static class GraalPyPrivate_Unicode_FillUtf8 extends CApiUnaryBuiltinNo @Specialization static Object doNative(PythonAbstractNativeObject s, - @Cached CStructAccess.WriteLongNode writeLongNode, @Cached EncodeNativeStringNode encodeNativeStringNode, - @Cached CStructAccess.WritePointerNode writePointerNode, - @Cached CStructAccess.AllocateNode allocateNode, @Cached CStructAccess.WriteTruffleStringNode writeTruffleStringNode) { TruffleString utf8Str = encodeNativeStringNode.execute(UTF_8, s, T_STRICT); int len = utf8Str.byteLength(UTF_8); - Object mem = allocateNode.alloc(len + 1, true); + long mem = CStructAccess.allocatePyMem(len + 1); writeTruffleStringNode.write(mem, utf8Str, UTF_8); - writePointerNode.writeToObj(s, CFields.PyCompactUnicodeObject__utf8, mem); - writeLongNode.writeToObject(s, CFields.PyCompactUnicodeObject__utf8_length, len); + writePtrField(s.getPtr(), CFields.PyCompactUnicodeObject__utf8, mem); + writeLongField(s.getPtr(), CFields.PyCompactUnicodeObject__utf8_length, len); return 0; } } @@ -1141,12 +1166,10 @@ static Object doNative(PythonAbstractNativeObject s, abstract static class GraalPyPrivate_Unicode_AsUnicodeAndSize extends CApiBinaryBuiltinNode { @Specialization - static Object doUnicode(PString s, Object sizePtr, + static long doUnicode(PString s, long sizePtr, @Bind Node inliningTarget, - @CachedLibrary(limit = "2") InteropLibrary lib, @Cached InlinedConditionProfile hasSizeProfile, @Cached InlinedConditionProfile hasUnicodeProfile, - @Cached CStructAccess.WriteLongNode writeLongNode, @Cached UnicodeAsWideCharNode asWideCharNode, @Cached HiddenAttr.ReadNode readAttrNode, @Cached HiddenAttr.WriteNode writeAttrNode) { @@ -1156,8 +1179,8 @@ static Object doUnicode(PString s, Object sizePtr, wcharBytes = asWideCharNode.executeNativeOrder(inliningTarget, s, wcharSize); s.setWCharBytes(inliningTarget, writeAttrNode, wcharBytes); } - if (hasSizeProfile.profile(inliningTarget, !lib.isNull(sizePtr))) { - writeLongNode.write(sizePtr, wcharBytes.getSequenceStorage().length() / wcharSize); + if (hasSizeProfile.profile(inliningTarget, sizePtr != NULLPTR)) { + NativeMemory.writeLong(sizePtr, wcharBytes.getSequenceStorage().length() / wcharSize); } return PySequenceArrayWrapper.ensureNativeSequence(wcharBytes); } @@ -1184,6 +1207,7 @@ static Object other(@SuppressWarnings("unused") Object s) { } } + // TODO(native-access) Remove or fix and add test for GraalPyPrivate_Unicode_FillUnicode @CApiBuiltin(ret = Int, args = {PyObject}, call = Ignored) abstract static class GraalPyPrivate_Unicode_FillUnicode extends CApiUnaryBuiltinNode { public static final int WCHAR_T_SIZE = PythonLanguage.getPythonOS() == PythonOS.PLATFORM_WIN32 ? 2 : 4; @@ -1194,16 +1218,17 @@ static Object doNative(PythonAbstractNativeObject s, @Bind Node inliningTarget, @Cached CastToTruffleStringNode cast, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, - @Cached CStructAccess.AllocateNode allocateNode, @Cached CStructAccess.WriteTruffleStringNode writeTruffleStringNode) { TruffleString str = switchEncodingNode.execute(cast.castKnownString(inliningTarget, s), WCHAR_T_ENCODING); int len = str.byteLength(WCHAR_T_ENCODING); - Object mem = allocateNode.alloc(len + WCHAR_T_SIZE, true); + long mem = CStructAccess.allocatePyMem(len + WCHAR_T_SIZE); writeTruffleStringNode.write(mem, str, WCHAR_T_ENCODING); + // writePtrField(s.getPtr(), CFields.PyASCIIObject__wstr, mem); return 0; } } + // TODO(native-access) Remove GraalPyPrivate_Unicode_AsWideChar @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, Int}, call = Ignored) abstract static class GraalPyPrivate_Unicode_AsWideChar extends CApiBinaryBuiltinNode { @Specialization @@ -1226,16 +1251,6 @@ static Object doUnicode(Object s, int elementSize, } } - @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtrAsTruffleString, VA_LIST_PTR}, call = CApiCallPath.Ignored) - abstract static class GraalPyPrivate_Unicode_FromFormat extends CApiBinaryBuiltinNode { - @Specialization - static Object doGeneric(TruffleString format, Object vaList, - @Bind Node inliningTarget, - @Cached UnicodeFromFormatNode unicodeFromFormatNode) { - return unicodeFromFormatNode.execute(inliningTarget, format, vaList); - } - } - @CApiBuiltin(ret = _PY_ERROR_HANDLER, args = {ConstCharPtrAsTruffleString}, call = Direct) abstract static class _Py_GetErrorHandler extends CApiUnaryBuiltinNode { @Specialization @@ -1254,16 +1269,13 @@ static Object doNull(@SuppressWarnings("unused") PNone noValue) { @CApiBuiltin(ret = PyObjectTransfer, args = {ConstCharPtrAsTruffleString, ConstCharPtr, Py_ssize_t, Py_ssize_t, Py_ssize_t, ConstCharPtrAsTruffleString}, call = Direct) abstract static class PyUnicodeDecodeError_Create extends CApi6BuiltinNode { @Specialization - static Object doit(Object encoding, Object object, long length, long start, long end, Object reason, + static Object doit(Object encoding, long object, long length, long start, long end, Object reason, @Bind Node inliningTarget, - @Cached GetByteArrayNode getByteArrayNode, @Cached CallNode callNode, @Cached PRaiseNode raiseNode) { PBytes bytes; try { - bytes = PFactory.createBytes(PythonLanguage.get(inliningTarget), getByteArrayNode.execute(inliningTarget, object, length)); - } catch (InteropException e) { - throw raiseNode.raise(inliningTarget, PythonErrorType.TypeError, ErrorMessages.M, e); + bytes = PFactory.createBytes(PythonLanguage.get(inliningTarget), getByteArray(object, length)); } catch (OverflowException e) { throw raiseNode.raise(inliningTarget, PythonErrorType.SystemError, ErrorMessages.NEGATIVE_SIZE_PASSED); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextWeakrefBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextWeakrefBuiltins.java index c25e63ce78..5baaf836c6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextWeakrefBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextWeakrefBuiltins.java @@ -43,40 +43,37 @@ import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PYWEAKREFERENCE_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.PyObjectBorrowed; +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.Void; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.nodes.BuiltinNames.T_PROXY_TYPE; import static com.oracle.graal.python.nodes.BuiltinNames.T__WEAKREF; import com.oracle.graal.python.builtins.PythonBuiltinClassType; 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.CApiUnaryBuiltinNode; import com.oracle.graal.python.builtins.modules.weakref.PProxyType; import com.oracle.graal.python.builtins.objects.PNone; +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.module.PythonModule; import com.oracle.graal.python.builtins.objects.referencetype.PReferenceType; import com.oracle.graal.python.builtins.objects.referencetype.ReferenceTypeBuiltins.ReferenceTypeNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; import com.oracle.graal.python.runtime.PythonContext; -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; public final class PythonCextWeakrefBuiltins { - @CApiBuiltin(ret = Void, args = {PyObject}, call = Direct) - abstract static class PyObject_ClearWeakRefs extends CApiUnaryBuiltinNode { - @Specialization - static Object warn(@SuppressWarnings("unused") Object ref) { - // TODO: implement - return PNone.NONE; - } + @CApiBuiltin(ret = Void, args = {PyObjectRawPointer}, call = Direct, acquireGil = false, canRaise = false) + public static void PyObject_ClearWeakRefs(@SuppressWarnings("unused") long pyObject) { + // TODO: Implement } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct) + @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct, acquireGil = false, canRaise = true) abstract static class PyWeakref_NewRef extends CApiBinaryBuiltinNode { @Specialization static Object refType(Object object, Object callback, @@ -85,46 +82,44 @@ static Object refType(Object object, Object callback, } } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, PyObject}, call = Direct) - abstract static class PyWeakref_NewProxy extends CApiBinaryBuiltinNode { - @Specialization - static Object refType(Object object, Object callback, - @Bind Node inliningTarget, - @Cached PyObjectCallMethodObjArgs call) { - PythonModule weakrefModule = PythonContext.get(inliningTarget).lookupBuiltinModule(T__WEAKREF); - return call.execute(null, inliningTarget, weakrefModule, T_PROXY_TYPE, object, callback == PNone.NO_VALUE ? PNone.NONE : callback); + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer, PyObjectRawPointer}, call = Direct, acquireGil = false, canRaise = true) + public static long PyWeakref_NewProxy(long objectPtr, long callbackPtr) { + PythonModule weakrefModule = PythonContext.get(null).lookupBuiltinModule(T__WEAKREF); + Object callback; + if (callbackPtr == NULLPTR) { + callback = PNone.NO_VALUE; + } else { + callback = NativeToPythonNode.executeRawUncached(callbackPtr); } + Object object = NativeToPythonNode.executeRawUncached(objectPtr); + Object proxy = PyObjectCallMethodObjArgs.executeUncached(weakrefModule, T_PROXY_TYPE, object, callback); + return PythonToNativeNewRefNode.executeLongUncached(proxy); } - @CApiBuiltin(ret = PyObjectBorrowed, args = {PyObject}, call = Direct) - abstract static class PyWeakref_GetObject extends CApiUnaryBuiltinNode { - @Specialization - static Object call(Object reference, - @Bind Node inliningTarget) { - if (reference instanceof PReferenceType ref) { - return ref.getPyObject(); - } - if (reference instanceof PProxyType proxy) { - PReferenceType ref = proxy.weakReference; - if (ref != null) { - return ref.getPyObject(); - } + @CApiBuiltin(ret = PyObjectRawPointer, args = {PyObjectRawPointer}, call = Direct, acquireGil = false) + public static long PyWeakref_GetObject(long referencePtr) { + Object reference = NativeToPythonNode.executeRawUncached(referencePtr); + if (reference instanceof PReferenceType ref) { + return ToNativeBorrowedNode.executeUncached(ref.getPyObject()); + } else if (reference instanceof PProxyType proxy) { + PReferenceType ref = proxy.weakReference; + if (ref != null) { + return ToNativeBorrowedNode.executeUncached(ref.getPyObject()); } - /* - * This weak reference has died in the managed side due to its referent being collected. - */ - return PNone.NONE; + } else { + throw PythonCextBuiltins.badInternalCall("PyWeakref_GetObject", "referencePtr"); } + /* + * This weak reference has died in the managed side due to its referent being collected. + */ + return PythonContext.get(null).getCApiContext().getNonePtr(); } - @CApiBuiltin(name = "_PyWeakref_ClearRef", ret = Void, args = {PYWEAKREFERENCE_PTR}, call = Direct) - abstract static class PyWeakref_ClearRef extends CApiUnaryBuiltinNode { - @Specialization - static Object call(Object reference) { - if (reference instanceof PReferenceType ref) { - ref.clearRef(); - } - return PNone.NONE; + @CApiBuiltin(name = "_PyWeakref_ClearRef", ret = Void, args = {PYWEAKREFERENCE_PTR}, call = Direct, acquireGil = false, canRaise = false) + public static void PyWeakref_ClearRef(long referencePtr) { + Object reference = NativeToPythonNode.executeRawUncached(referencePtr); + if (reference instanceof PReferenceType ref) { + ref.clearRef(); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsCNModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsCNModuleBuiltins.java index 624b65398c..f82d16cdef 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsCNModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsCNModuleBuiltins.java @@ -41,11 +41,9 @@ package com.oracle.graal.python.builtins.modules.cjkcodecs; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodecUtil.findCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.PyMultibyteCodec_CAPSULE_NAME; +import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.createCodec; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.registerCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.CreateCodecNode.createCodec; import static com.oracle.graal.python.nodes.BuiltinNames.J__CODECS_CN; -import static com.oracle.graal.python.nodes.BuiltinNames.T__CODECS_CN; import static com.oracle.graal.python.nodes.ErrorMessages.ENCODING_NAME_MUST_BE_A_STRING; import static com.oracle.graal.python.nodes.ErrorMessages.NO_SUCH_CODEC_IS_SUPPORTED; import static com.oracle.graal.python.runtime.exception.PythonErrorType.LookupError; @@ -53,21 +51,17 @@ import java.util.List; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.DBCSMap.MappingType; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodec.CodecType; -import com.oracle.graal.python.builtins.objects.capsule.PyCapsule; -import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -104,15 +98,13 @@ public void initialize(Python3Core core) { @Override public void postInitialize(Python3Core core) { super.postInitialize(core); - PythonLanguage language = core.getLanguage(); - PythonModule codec = core.lookupBuiltinModule(T__CODECS_CN); - registerCodec("gb2312", 0, CodecType.STATELESS, 0, MappingType.DECONLY, MAPPING_LIST, CODEC_LIST, codec, language); - registerCodec("gbk", 1, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("gb18030", 2, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("hz", 3, CodecType.STATEFUL, -1, null, null, CODEC_LIST, codec, language); - registerCodec("gbkext", -1, null, 1, MappingType.DECONLY, MAPPING_LIST, null, codec, language); - registerCodec("gbcommon", -1, null, 2, MappingType.ENCONLY, MAPPING_LIST, null, codec, language); - registerCodec("gb18030ext", -1, null, 3, MappingType.ENCDEC, MAPPING_LIST, null, codec, language); + registerCodec("gb2312", 0, CodecType.STATELESS, 0, MappingType.DECONLY, MAPPING_LIST, CODEC_LIST); + registerCodec("gbk", 1, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("gb18030", 2, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("hz", 3, CodecType.STATEFUL, -1, null, null, CODEC_LIST); + registerCodec("gbkext", -1, null, 1, MappingType.DECONLY, MAPPING_LIST, null); + registerCodec("gbcommon", -1, null, 2, MappingType.ENCONLY, MAPPING_LIST, null); + registerCodec("gb18030ext", -1, null, 3, MappingType.ENCDEC, MAPPING_LIST, null); } @Builtin(name = "getcodec", minNumOfPositionalArgs = 1) @@ -125,7 +117,6 @@ static Object getcodec(Object encoding, @Cached TruffleString.EqualNode isEqual, @Cached PyUnicodeCheckNode unicodeCheckNode, @Cached CastToTruffleStringNode asUTF8Node, - @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { if (!unicodeCheckNode.execute(inliningTarget, encoding)) { @@ -137,8 +128,7 @@ static Object getcodec(Object encoding, throw raiseNode.raise(inliningTarget, LookupError, NO_SUCH_CODEC_IS_SUPPORTED); } - PyCapsule codecobj = PFactory.createCapsuleJavaName(language, codec, PyMultibyteCodec_CAPSULE_NAME); - return createCodec(inliningTarget, codecobj, raiseNode); + return createCodec(inliningTarget, codec); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsHKModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsHKModuleBuiltins.java index 57a8fe9d46..2f1fca9d96 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsHKModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsHKModuleBuiltins.java @@ -41,11 +41,9 @@ package com.oracle.graal.python.builtins.modules.cjkcodecs; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodecUtil.findCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.PyMultibyteCodec_CAPSULE_NAME; +import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.createCodec; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.registerCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.CreateCodecNode.createCodec; import static com.oracle.graal.python.nodes.BuiltinNames.J__CODECS_HK; -import static com.oracle.graal.python.nodes.BuiltinNames.T__CODECS_HK; import static com.oracle.graal.python.nodes.ErrorMessages.ENCODING_NAME_MUST_BE_A_STRING; import static com.oracle.graal.python.nodes.ErrorMessages.NO_SUCH_CODEC_IS_SUPPORTED; import static com.oracle.graal.python.runtime.exception.PythonErrorType.LookupError; @@ -53,21 +51,17 @@ import java.util.List; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.DBCSMap.MappingType; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodec.CodecType; -import com.oracle.graal.python.builtins.objects.capsule.PyCapsule; -import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -100,11 +94,9 @@ public void initialize(Python3Core core) { @Override public void postInitialize(Python3Core core) { super.postInitialize(core); - PythonLanguage language = core.getLanguage(); - PythonModule codec = core.lookupBuiltinModule(T__CODECS_HK); - registerCodec("big5hkscs", 0, CodecType.STATELESS_WINIT, 0, MappingType.DECONLY, MAPPING_LIST, CODEC_LIST, codec, language); - registerCodec("big5hkscs_bmp", -1, null, 1, MappingType.ENCONLY, MAPPING_LIST, CODEC_LIST, codec, language); - registerCodec("big5hkscs_nonbmp", -1, null, 2, MappingType.ENCONLY, MAPPING_LIST, CODEC_LIST, codec, language); + registerCodec("big5hkscs", 0, CodecType.STATELESS_WINIT, 0, MappingType.DECONLY, MAPPING_LIST, CODEC_LIST); + registerCodec("big5hkscs_bmp", -1, null, 1, MappingType.ENCONLY, MAPPING_LIST, CODEC_LIST); + registerCodec("big5hkscs_nonbmp", -1, null, 2, MappingType.ENCONLY, MAPPING_LIST, CODEC_LIST); } @Builtin(name = "getcodec", minNumOfPositionalArgs = 1) @@ -117,7 +109,6 @@ static Object getcodec(Object encoding, @Cached TruffleString.EqualNode isEqual, @Cached PyUnicodeCheckNode unicodeCheckNode, @Cached CastToTruffleStringNode asUTF8Node, - @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { if (!unicodeCheckNode.execute(inliningTarget, encoding)) { @@ -129,8 +120,7 @@ static Object getcodec(Object encoding, throw raiseNode.raise(inliningTarget, LookupError, NO_SUCH_CODEC_IS_SUPPORTED); } - PyCapsule codecobj = PFactory.createCapsuleJavaName(language, codec, PyMultibyteCodec_CAPSULE_NAME); - return createCodec(inliningTarget, codecobj, raiseNode); + return createCodec(inliningTarget, codec); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsISO2022ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsISO2022ModuleBuiltins.java index 6c0279168a..eb2e18f415 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsISO2022ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsISO2022ModuleBuiltins.java @@ -41,11 +41,9 @@ package com.oracle.graal.python.builtins.modules.cjkcodecs; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodecUtil.findCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.PyMultibyteCodec_CAPSULE_NAME; +import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.createCodec; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.registerCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.CreateCodecNode.createCodec; import static com.oracle.graal.python.nodes.BuiltinNames.J__CODECS_ISO2022; -import static com.oracle.graal.python.nodes.BuiltinNames.T__CODECS_ISO2022; import static com.oracle.graal.python.nodes.ErrorMessages.ENCODING_NAME_MUST_BE_A_STRING; import static com.oracle.graal.python.nodes.ErrorMessages.NO_SUCH_CODEC_IS_SUPPORTED; import static com.oracle.graal.python.runtime.exception.PythonErrorType.LookupError; @@ -53,20 +51,16 @@ import java.util.List; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodec.CodecType; -import com.oracle.graal.python.builtins.objects.capsule.PyCapsule; -import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -100,16 +94,14 @@ protected List> getNodeFa @Override public void postInitialize(Python3Core core) { super.postInitialize(core); - PythonLanguage language = core.getLanguage(); - PythonModule codec = core.lookupBuiltinModule(T__CODECS_ISO2022); int i = 0; - registerCodec("iso2022_kr", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST, codec, language); - registerCodec("iso2022_jp", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST, codec, language); - registerCodec("iso2022_jp_1", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST, codec, language); - registerCodec("iso2022_jp_2", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST, codec, language); - registerCodec("iso2022_jp_2004", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST, codec, language); - registerCodec("iso2022_jp_3", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST, codec, language); - registerCodec("iso2022_jp_ext", i, CodecType.ISO2022, -1, null, null, CODEC_LIST, codec, language); + registerCodec("iso2022_kr", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST); + registerCodec("iso2022_jp", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST); + registerCodec("iso2022_jp_1", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST); + registerCodec("iso2022_jp_2", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST); + registerCodec("iso2022_jp_2004", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST); + registerCodec("iso2022_jp_3", i++, CodecType.ISO2022, -1, null, null, CODEC_LIST); + registerCodec("iso2022_jp_ext", i, CodecType.ISO2022, -1, null, null, CODEC_LIST); } @Override @@ -127,7 +119,6 @@ static Object getcodec(Object encoding, @Cached TruffleString.EqualNode isEqual, @Cached PyUnicodeCheckNode unicodeCheckNode, @Cached CastToTruffleStringNode asUTF8Node, - @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { if (!unicodeCheckNode.execute(inliningTarget, encoding)) { @@ -139,8 +130,7 @@ static Object getcodec(Object encoding, throw raiseNode.raise(inliningTarget, LookupError, NO_SUCH_CODEC_IS_SUPPORTED); } - PyCapsule codecobj = PFactory.createCapsuleJavaName(language, codec, PyMultibyteCodec_CAPSULE_NAME); - return createCodec(inliningTarget, codecobj, raiseNode); + return createCodec(inliningTarget, codec); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsJPModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsJPModuleBuiltins.java index 3f25996660..de0b819bc8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsJPModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsJPModuleBuiltins.java @@ -41,11 +41,9 @@ package com.oracle.graal.python.builtins.modules.cjkcodecs; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodecUtil.findCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.PyMultibyteCodec_CAPSULE_NAME; +import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.createCodec; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.registerCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.CreateCodecNode.createCodec; import static com.oracle.graal.python.nodes.BuiltinNames.J__CODECS_JP; -import static com.oracle.graal.python.nodes.BuiltinNames.T__CODECS_JP; import static com.oracle.graal.python.nodes.ErrorMessages.ENCODING_NAME_MUST_BE_A_STRING; import static com.oracle.graal.python.nodes.ErrorMessages.NO_SUCH_CODEC_IS_SUPPORTED; import static com.oracle.graal.python.runtime.exception.PythonErrorType.LookupError; @@ -53,21 +51,17 @@ import java.util.List; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.DBCSMap.MappingType; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodec.CodecType; -import com.oracle.graal.python.builtins.objects.capsule.PyCapsule; -import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -109,29 +103,27 @@ protected List> getNodeFa @Override public void postInitialize(Python3Core core) { super.postInitialize(core); - PythonLanguage language = core.getLanguage(); - PythonModule codec = core.lookupBuiltinModule(T__CODECS_JP); int i = 0; - registerCodec("shift_jis", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("cp932", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("euc_jp", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("shift_jis_2004", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("euc_jis_2004", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("euc_jisx0213", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("shift_jisx0213", i, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); + registerCodec("shift_jis", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("cp932", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("euc_jp", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("shift_jis_2004", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("euc_jis_2004", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("euc_jisx0213", i++, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("shift_jisx0213", i, CodecType.STATELESS, -1, null, null, CODEC_LIST); i = 0; - registerCodec("jisx0208", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisx0212", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisxcommon", -1, null, i++, MappingType.ENCONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisx0213_1_bmp", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisx0213_2_bmp", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisx0213_bmp", -1, null, i++, MappingType.ENCONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisx0213_1_emp", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisx0213_2_emp", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisx0213_emp", -1, null, i++, MappingType.ENCONLY, MAPPING_LIST, null, codec, language); - registerCodec("jisx0213_pair", -1, null, i++, MappingType.ENCDEC, MAPPING_LIST, null, codec, language); - registerCodec("cp932ext", -1, null, i, MappingType.ENCDEC, MAPPING_LIST, null, codec, language); + registerCodec("jisx0208", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null); + registerCodec("jisx0212", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null); + registerCodec("jisxcommon", -1, null, i++, MappingType.ENCONLY, MAPPING_LIST, null); + registerCodec("jisx0213_1_bmp", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null); + registerCodec("jisx0213_2_bmp", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null); + registerCodec("jisx0213_bmp", -1, null, i++, MappingType.ENCONLY, MAPPING_LIST, null); + registerCodec("jisx0213_1_emp", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null); + registerCodec("jisx0213_2_emp", -1, null, i++, MappingType.DECONLY, MAPPING_LIST, null); + registerCodec("jisx0213_emp", -1, null, i++, MappingType.ENCONLY, MAPPING_LIST, null); + registerCodec("jisx0213_pair", -1, null, i++, MappingType.ENCDEC, MAPPING_LIST, null); + registerCodec("cp932ext", -1, null, i, MappingType.ENCDEC, MAPPING_LIST, null); } @Override @@ -149,7 +141,6 @@ static Object getcodec(Object encoding, @Cached TruffleString.EqualNode isEqual, @Cached PyUnicodeCheckNode unicodeCheckNode, @Cached CastToTruffleStringNode asUTF8Node, - @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { if (!unicodeCheckNode.execute(inliningTarget, encoding)) { @@ -161,8 +152,7 @@ static Object getcodec(Object encoding, throw raiseNode.raise(inliningTarget, LookupError, NO_SUCH_CODEC_IS_SUPPORTED); } - PyCapsule codecobj = PFactory.createCapsuleJavaName(language, codec, PyMultibyteCodec_CAPSULE_NAME); - return createCodec(inliningTarget, codecobj, raiseNode); + return createCodec(inliningTarget, codec); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsKRModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsKRModuleBuiltins.java index f59c6cb254..b893732539 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsKRModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsKRModuleBuiltins.java @@ -41,11 +41,9 @@ package com.oracle.graal.python.builtins.modules.cjkcodecs; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodecUtil.findCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.PyMultibyteCodec_CAPSULE_NAME; +import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.createCodec; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.registerCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.CreateCodecNode.createCodec; import static com.oracle.graal.python.nodes.BuiltinNames.J__CODECS_KR; -import static com.oracle.graal.python.nodes.BuiltinNames.T__CODECS_KR; import static com.oracle.graal.python.nodes.ErrorMessages.ENCODING_NAME_MUST_BE_A_STRING; import static com.oracle.graal.python.nodes.ErrorMessages.NO_SUCH_CODEC_IS_SUPPORTED; import static com.oracle.graal.python.runtime.exception.PythonErrorType.LookupError; @@ -53,21 +51,17 @@ import java.util.List; -import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.DBCSMap.MappingType; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodec.CodecType; -import com.oracle.graal.python.builtins.objects.capsule.PyCapsule; -import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -97,14 +91,12 @@ protected List> getNodeFa @Override public void postInitialize(Python3Core core) { super.postInitialize(core); - PythonLanguage language = core.getLanguage(); - PythonModule codec = core.lookupBuiltinModule(T__CODECS_KR); - registerCodec("euc_kr", 0, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("cp949", 1, CodecType.STATELESS, 1, MappingType.ENCONLY, MAPPING_LIST, CODEC_LIST, codec, language); - registerCodec("johab", 2, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - - registerCodec("ksx1001", -1, null, 0, MappingType.DECONLY, MAPPING_LIST, null, codec, language); - registerCodec("cp949ext", -1, null, 2, MappingType.DECONLY, MAPPING_LIST, null, codec, language); + registerCodec("euc_kr", 0, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("cp949", 1, CodecType.STATELESS, 1, MappingType.ENCONLY, MAPPING_LIST, CODEC_LIST); + registerCodec("johab", 2, CodecType.STATELESS, -1, null, null, CODEC_LIST); + + registerCodec("ksx1001", -1, null, 0, MappingType.DECONLY, MAPPING_LIST, null); + registerCodec("cp949ext", -1, null, 2, MappingType.DECONLY, MAPPING_LIST, null); } @Override @@ -122,7 +114,6 @@ static Object getcodec(Object encoding, @Cached TruffleString.EqualNode isEqual, @Cached PyUnicodeCheckNode unicodeCheckNode, @Cached CastToTruffleStringNode asUTF8Node, - @Bind PythonLanguage language, @Cached PRaiseNode raiseNode) { if (!unicodeCheckNode.execute(inliningTarget, encoding)) { @@ -134,8 +125,7 @@ static Object getcodec(Object encoding, throw raiseNode.raise(inliningTarget, LookupError, NO_SUCH_CODEC_IS_SUPPORTED); } - PyCapsule codecobj = PFactory.createCapsuleJavaName(language, codec, PyMultibyteCodec_CAPSULE_NAME); - return createCodec(inliningTarget, codecobj, raiseNode); + return createCodec(inliningTarget, codec); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsTWModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsTWModuleBuiltins.java index 86d6dc61e0..a8ab202b4f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsTWModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/CodecsTWModuleBuiltins.java @@ -41,11 +41,9 @@ package com.oracle.graal.python.builtins.modules.cjkcodecs; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodecUtil.findCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.PyMultibyteCodec_CAPSULE_NAME; +import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.createCodec; import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.registerCodec; -import static com.oracle.graal.python.builtins.modules.cjkcodecs.MultibytecodecModuleBuiltins.CreateCodecNode.createCodec; import static com.oracle.graal.python.nodes.BuiltinNames.J__CODECS_TW; -import static com.oracle.graal.python.nodes.BuiltinNames.T__CODECS_TW; import static com.oracle.graal.python.nodes.ErrorMessages.ENCODING_NAME_MUST_BE_A_STRING; import static com.oracle.graal.python.nodes.ErrorMessages.NO_SUCH_CODEC_IS_SUPPORTED; import static com.oracle.graal.python.runtime.exception.PythonErrorType.LookupError; @@ -60,14 +58,11 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.DBCSMap.MappingType; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodec.CodecType; -import com.oracle.graal.python.builtins.objects.capsule.PyCapsule; -import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateNodeFactory; @@ -95,11 +90,9 @@ protected List> getNodeFa @Override public void postInitialize(Python3Core core) { super.postInitialize(core); - PythonLanguage language = core.getLanguage(); - PythonModule codec = core.lookupBuiltinModule(T__CODECS_TW); - registerCodec("big5", 0, CodecType.STATELESS, 0, MappingType.ENCDEC, MAPPING_LIST, CODEC_LIST, codec, language); - registerCodec("cp950", 1, CodecType.STATELESS, -1, null, null, CODEC_LIST, codec, language); - registerCodec("cp950ext", -1, null, 1, MappingType.ENCDEC, MAPPING_LIST, null, codec, language); + registerCodec("big5", 0, CodecType.STATELESS, 0, MappingType.ENCDEC, MAPPING_LIST, CODEC_LIST); + registerCodec("cp950", 1, CodecType.STATELESS, -1, null, null, CODEC_LIST); + registerCodec("cp950ext", -1, null, 1, MappingType.ENCDEC, MAPPING_LIST, null); } @Override @@ -129,8 +122,7 @@ static Object getcodec(Object encoding, throw raiseNode.raise(inliningTarget, LookupError, NO_SUCH_CODEC_IS_SUPPORTED); } - PyCapsule codecobj = PFactory.createCapsuleJavaName(language, codec, PyMultibyteCodec_CAPSULE_NAME); - return createCodec(inliningTarget, codecobj, raiseNode); + return createCodec(inliningTarget, codec); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibytecodecModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibytecodecModuleBuiltins.java index fc9bc375cd..e86fc644d9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibytecodecModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cjkcodecs/MultibytecodecModuleBuiltins.java @@ -40,65 +40,51 @@ */ package com.oracle.graal.python.builtins.modules.cjkcodecs; -import static com.oracle.graal.python.nodes.ErrorMessages.ARGUMENT_TYPE_INVALID; 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; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; +import java.util.ArrayList; import java.util.List; import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.cjkcodecs.DBCSMap.MappingType; import com.oracle.graal.python.builtins.modules.cjkcodecs.MultibyteCodec.CodecType; -import com.oracle.graal.python.builtins.objects.capsule.PyCapsule; -import com.oracle.graal.python.builtins.objects.module.PythonModule; -import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; -import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.util.CharsetMapping; -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.GenerateNodeFactory; 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.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @CoreFunctions(defineModule = "_multibytecodec") public final class MultibytecodecModuleBuiltins extends PythonBuiltins { - static final byte[] PyMultibyteCodec_CAPSULE_NAME = PyCapsule.capsuleName("multibytecodec.__map_*"); /** insufficient output buffer space */ - protected static final int MBERR_TOOSMALL = -1; + static final int MBERR_TOOSMALL = -1; /** incomplete input buffer */ - protected static final int MBERR_TOOFEW = -2; + static final int MBERR_TOOFEW = -2; /** internal runtime error */ - protected static final int MBERR_INTERNAL = -3; + static final int MBERR_INTERNAL = -3; - protected static final TruffleString ERROR_STRICT = T_STRICT; - protected static final TruffleString ERROR_IGNORE = T_IGNORE; - protected static final TruffleString ERROR_REPLACE = T_REPLACE; + static final TruffleString ERROR_STRICT = T_STRICT; + static final TruffleString ERROR_IGNORE = T_IGNORE; + static final TruffleString ERROR_REPLACE = T_REPLACE; static final int MBENC_FLUSH = 0x0001; /* encode all characters encodable */ public static final int MBENC_MAX = MBENC_FLUSH; @Override protected List> getNodeFactories() { - return MultibytecodecModuleBuiltinsFactory.getFactories(); + return new ArrayList<>(); } - protected static void registerCodec(String name, int cidx, CodecType ct, int midx, MappingType mt, - DBCSMap[] maps, MultibyteCodec[] codecs, - PythonModule codec, PythonLanguage language) { + static void registerCodec(String name, int cidx, CodecType ct, int midx, MappingType mt, + DBCSMap[] maps, MultibyteCodec[] codecs) { TruffleString tsName = toTruffleStringUncached(name); TruffleString normalizedEncoding = CharsetMapping.normalizeUncached(tsName); CharsetMapping.CharsetWrapper charset = CharsetMapping.getCharsetNormalized(normalizedEncoding); @@ -107,9 +93,7 @@ protected static void registerCodec(String name, int cidx, CodecType ct, int mid codecs[cidx] = new MultibyteCodec(tsName, charset.charset(), ct); } if (midx != -1) { - DBCSMap h = maps[midx] = new DBCSMap(name, tsName, charset.charset(), mt); - codec.setAttribute(toTruffleStringUncached(h.charsetMapName), - PFactory.createCapsuleJavaName(language, h, PyMultibyteCodec_CAPSULE_NAME)); + maps[midx] = new DBCSMap(name, tsName, charset.charset(), mt); } } } @@ -119,34 +103,8 @@ public void initialize(Python3Core core) { super.initialize(core); } - @Builtin(name = "__create_codec", minNumOfPositionalArgs = 1, doc = "__create_codec($module, arg, /)\n--\n\n") - @GenerateNodeFactory - abstract static class CreateCodecNode extends PythonUnaryBuiltinNode { - - @Specialization - static Object createCodec(PyCapsule arg, - @Bind Node inliningTarget, - @Cached PRaiseNode raiseNode) { - return createCodec(inliningTarget, arg, raiseNode); - } - - static Object createCodec(Node inliningTarget, PyCapsule arg, - PRaiseNode raiseNode) { - if (!PyCapsule.capsuleJavaNameIs(arg, PyMultibyteCodec_CAPSULE_NAME)) { - throw raiseNode.raise(inliningTarget, ValueError, ARGUMENT_TYPE_INVALID); - } - MultibyteCodec codec; - codec = (MultibyteCodec) arg.getPointer(); - codec.codecinit(); - return PFactory.createMultibyteCodecObject(PythonLanguage.get(inliningTarget), codec); - } - - @Fallback - static Object createCodec(@SuppressWarnings("unused") VirtualFrame frame, @SuppressWarnings("unused") Object arg, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ARGUMENT_TYPE_INVALID); - } - + static Object createCodec(Node inliningTarget, MultibyteCodec codec) { + codec.codecinit(); + return PFactory.createMultibyteCodecObject(PythonLanguage.get(inliningTarget), codec); } - } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java index e38f391955..a37100e563 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateBuiltins.java @@ -89,7 +89,6 @@ 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.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; @@ -463,9 +462,8 @@ static int getYear(PDate self) { } @Specialization - static int getYear(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.FromNative.getYear(self, readNode); + static int getYear(PythonAbstractNativeObject self) { + return DateNodes.FromNative.getYear(self); } @Specialization @@ -486,9 +484,8 @@ static int getMonth(PDate self) { } @Specialization - static int getMonth(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.FromNative.getMonth(self, readNode); + static int getMonth(PythonAbstractNativeObject self) { + return DateNodes.FromNative.getMonth(self); } @Specialization @@ -509,9 +506,8 @@ static int getDay(PDate self) { } @Specialization - static int getDay(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateNodes.FromNative.getDay(self, readNode); + static int getDay(PythonAbstractNativeObject self) { + return DateNodes.FromNative.getDay(self); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java index 9d5d0e43fd..3615848c65 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateNodes.java @@ -44,22 +44,28 @@ import static com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins.MAX_YEAR; import static com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins.MIN_YEAR; +import java.lang.ref.Reference; import java.time.YearMonth; +import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; 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; 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.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyLongAsIntNode; +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; import com.oracle.graal.python.nodes.call.CallNode; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; @@ -127,6 +133,7 @@ private static int getMaxDayOfMonth(int year, int month) { @GenerateInline @GenerateCached(false) public abstract static class NewUnsafeNode extends Node { + private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_DATE_SUBTYPE_NEW); public abstract Object execute(Node inliningTarget, Object cls, int year, int month, int day); @@ -138,17 +145,24 @@ public static Object executeUncached(Object cls, int year, int month, int day) { static Object newDate(Node inliningTarget, Object cls, int year, int month, int day, @Cached TypeNodes.GetInstanceShape getInstanceShape, @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, - @Cached CExtNodes.PCallCapiFunction callCapiFunction, - @Cached ExternalFunctionNodes.DefaultCheckFunctionResultNode checkFunctionResultNode, + @Cached ExternalFunctionNodes.PyObjectCheckFunctionResultNode checkFunctionResultNode, @Cached CApiTransitions.PythonToNativeNode toNativeNode, @Cached CApiTransitions.NativeToPythonTransferNode fromNativeNode) { if (!needsNativeAllocationNode.execute(inliningTarget, cls)) { Shape shape = getInstanceShape.execute(cls); return new PDate(cls, shape, year, month, day); } else { - Object nativeResult = callCapiFunction.call(NativeCAPISymbol.FUN_DATE_SUBTYPE_NEW, toNativeNode.execute(cls), year, month, day); - checkFunctionResultNode.execute(PythonContext.get(inliningTarget), NativeCAPISymbol.FUN_DATE_SUBTYPE_NEW.getTsName(), nativeResult); - return fromNativeNode.execute(nativeResult); + long clsPointer = toNativeNode.executeLong(cls); + try { + PythonContext context = PythonContext.get(inliningTarget); + var callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_DATE_SUBTYPE_NEW); + long nativeResult = ExternalFunctionInvoker.invokeDATE_SUBTYPE_NEW(null, C_API_TIMING, + context.ensureNativeContext(), BoundaryCallData.getUncached(), context.getThreadState(PythonLanguage.get(inliningTarget)), callable, clsPointer, + year, month, day); + return checkFunctionResultNode.execute(context, NativeCAPISymbol.FUN_DATE_SUBTYPE_NEW.getTsName(), fromNativeNode.execute(nativeResult)); + } finally { + Reference.reachabilityFence(cls); + } } } } @@ -182,18 +196,21 @@ static boolean isBuiltinClass(Object cls) { } public static final class FromNative { - static int getYear(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - int b0 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 0); - int b1 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 1); + static int getYear(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Date__data); + int b0 = NativeMemory.readByteArrayElement(ptr, 0) & 0xFF; + int b1 = NativeMemory.readByteArrayElement(ptr, 1) & 0xFF; return b0 << 8 | b1; } - static int getMonth(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 2); + static int getMonth(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Date__data); + return NativeMemory.readByteArrayElement(ptr, 2) & 0xFF; } - static int getDay(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Date__data, 3); + static int getDay(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Date__data); + return NativeMemory.readByteArrayElement(ptr, 3) & 0xFF; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java index 64f7b83c64..4fc7a42e6b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java @@ -110,7 +110,6 @@ 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.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; @@ -864,9 +863,8 @@ static int getHour(PDateTime self) { } @Specialization - static int getHour(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readByteNode) { - return DateTimeNodes.FromNative.getHour(self, readByteNode); + static int getHour(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getHour(self); } @Specialization @@ -887,9 +885,8 @@ static int getMinute(PDateTime self) { } @Specialization - static int getMinute(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.FromNative.getMinute(self, readNode); + static int getMinute(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getMinute(self); } @Specialization @@ -910,9 +907,8 @@ static int getSecond(PDateTime self) { } @Specialization - static int getSecond(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.FromNative.getSecond(self, readNode); + static int getSecond(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getSecond(self); } @Specialization @@ -933,9 +929,8 @@ static int getMicrosecond(PDateTime self) { } @Specialization - static int getMicrosecond(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.FromNative.getMicrosecond(self, readNode); + static int getMicrosecond(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getMicrosecond(self); } @Specialization @@ -969,9 +964,8 @@ static int getFold(PDateTime self) { } @Specialization - static int getFold(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return DateTimeNodes.FromNative.getFold(self, readNode); + static int getFold(PythonAbstractNativeObject self) { + return DateTimeNodes.FromNative.getFold(self); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java index b2ad472e00..bc3dea3ed9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeNodes.java @@ -44,27 +44,33 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins.MAX_YEAR; import static com.oracle.graal.python.builtins.modules.datetime.DatetimeModuleBuiltins.MIN_YEAR; +import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readByteField; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; +import java.lang.ref.Reference; import java.time.YearMonth; 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.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; 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.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.type.TypeNodes; -import com.oracle.graal.python.lib.PyTZInfoCheckNode; import com.oracle.graal.python.lib.PyLongAsIntNode; +import com.oracle.graal.python.lib.PyTZInfoCheckNode; +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; import com.oracle.graal.python.nodes.call.CallNode; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; @@ -209,6 +215,7 @@ private static int getMaxDayOfMonth(int year, int month) { @GenerateInline @GenerateCached(false) public abstract static class NewUnsafeNode extends Node { + private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_DATETIME_SUBTYPE_NEW); public abstract Object execute(Node inliningTarget, Object cls, int year, int month, int day, int hour, int minute, int second, int microsecond, Object tzInfoObject, int fold); @@ -222,8 +229,7 @@ static Object newDateTime(Node inliningTarget, Object cls, int year, int month, @Cached PyTZInfoCheckNode tzInfoCheckNode, @Cached TypeNodes.GetInstanceShape getInstanceShape, @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, - @Cached CExtNodes.PCallCapiFunction callCapiFunction, - @Cached ExternalFunctionNodes.DefaultCheckFunctionResultNode checkFunctionResultNode, + @Cached ExternalFunctionNodes.PyObjectCheckFunctionResultNode checkFunctionResultNode, @Cached CApiTransitions.PythonToNativeNode toNativeNode, @Cached CApiTransitions.NativeToPythonTransferNode fromNativeNode) { // create DateTime without thorough validation @@ -244,10 +250,20 @@ static Object newDateTime(Node inliningTarget, Object cls, int year, int month, Shape shape = getInstanceShape.execute(cls); return new PDateTime(cls, shape, year, month, day, hour, minute, second, microsecond, tzInfo, fold); } else { - Object nativeResult = callCapiFunction.call(NativeCAPISymbol.FUN_DATETIME_SUBTYPE_NEW, - toNativeNode.execute(cls), year, month, day, hour, minute, second, microsecond, toNativeNode.execute(tzInfo != null ? tzInfo : PNone.NO_VALUE), fold); - checkFunctionResultNode.execute(PythonContext.get(inliningTarget), NativeCAPISymbol.FUN_DATETIME_SUBTYPE_NEW.getTsName(), nativeResult); - return fromNativeNode.execute(nativeResult); + long clsPointer = toNativeNode.executeLong(cls); + Object effectiveTzInfo = tzInfo != null ? tzInfo : PNone.NO_VALUE; + long tzInfoPointer = toNativeNode.executeLong(effectiveTzInfo); + try { + PythonContext context = PythonContext.get(inliningTarget); + var callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_DATETIME_SUBTYPE_NEW); + long nativeResult = ExternalFunctionInvoker.invokeDATETIME_SUBTYPE_NEW(null, C_API_TIMING, + context.ensureNativeContext(), BoundaryCallData.getUncached(), context.getThreadState(context.getLanguage(inliningTarget)), callable, clsPointer, + year, month, day, hour, minute, second, microsecond, tzInfoPointer, fold); + return checkFunctionResultNode.execute(context, NativeCAPISymbol.FUN_DATETIME_SUBTYPE_NEW.getTsName(), fromNativeNode.execute(nativeResult)); + } finally { + Reference.reachabilityFence(cls); + Reference.reachabilityFence(effectiveTzInfo); + } } } } @@ -291,42 +307,49 @@ static boolean isBuiltinClass(Object cls) { } public static final class FromNative { - static int getYear(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - int b0 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 0); - int b1 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 1); + static int getYear(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_DateTime__data); + int b0 = NativeMemory.readByteArrayElement(ptr, 0) & 0xFF; + int b1 = NativeMemory.readByteArrayElement(ptr, 1) & 0xFF; return (b0 << 8) | b1; } - static int getMonth(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 2); + static int getMonth(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_DateTime__data); + return NativeMemory.readByteArrayElement(ptr, 2) & 0xFF; } - static int getDay(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 3); + static int getDay(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_DateTime__data); + return NativeMemory.readByteArrayElement(ptr, 3) & 0xFF; } - static int getHour(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 4); + static int getHour(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_DateTime__data); + return NativeMemory.readByteArrayElement(ptr, 4) & 0xFF; } - static int getMinute(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 5); + static int getMinute(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_DateTime__data); + return NativeMemory.readByteArrayElement(ptr, 5) & 0xFF; } - static int getSecond(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 6); + static int getSecond(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_DateTime__data); + return NativeMemory.readByteArrayElement(ptr, 6) & 0xFF; } - static int getMicrosecond(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - int b3 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 7); - int b4 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 8); - int b5 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__data, 9); + static int getMicrosecond(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_DateTime__data); + int b3 = NativeMemory.readByteArrayElement(ptr, 7) & 0xFF; + int b4 = NativeMemory.readByteArrayElement(ptr, 8) & 0xFF; + int b5 = NativeMemory.readByteArrayElement(ptr, 9) & 0xFF; return (b3 << 16) | (b4 << 8) | b5; } - static Object getTzInfo(PythonAbstractNativeObject obj, CStructAccess.ReadByteNode readByteNode, CStructAccess.ReadObjectNode readObjectNode) { + static Object getTzInfo(PythonAbstractNativeObject obj, CStructAccess.ReadObjectNode readObjectNode) { Object tzInfo = null; - if (readByteNode.readFromObj(obj, CFields.PyDateTime_DateTime__hastzinfo) != 0) { + if (readByteField(obj.getPtr(), CFields.PyDateTime_DateTime__hastzinfo) != 0) { Object tzinfoObj = readObjectNode.readFromObj(obj, CFields.PyDateTime_DateTime__tzinfo); if (tzinfoObj != PNone.NO_VALUE) { tzInfo = tzinfoObj; @@ -335,8 +358,8 @@ static Object getTzInfo(PythonAbstractNativeObject obj, CStructAccess.ReadByteNo return tzInfo; } - static int getFold(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_DateTime__fold); + static int getFold(PythonAbstractNativeObject self) { + return readByteField(self.getPtr(), CFields.PyDateTime_DateTime__fold); } } @@ -358,9 +381,8 @@ static Object getTzInfo(PDateTime self) { @Specialization static Object getTzInfo(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return FromNative.getTzInfo(self, readByteNode, readObjectNode); + return FromNative.getTzInfo(self, readObjectNode); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java index 0ec7b88917..5cfef07b86 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TemporalValueNodes.java @@ -62,8 +62,8 @@ import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.CompilerDirectives.ValueType; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; @@ -274,10 +274,9 @@ static TimeDeltaValue doManaged(PTimeDelta value) { @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") static TimeDeltaValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, - @SuppressWarnings("unused") @Cached PyDeltaCheckNode checkNode, - @Cached CStructAccess.ReadI32Node readIntNode) { - return new TimeDeltaValue(TimeDeltaNodes.FromNative.getDays(value, readIntNode), TimeDeltaNodes.FromNative.getSeconds(value, readIntNode), - TimeDeltaNodes.FromNative.getMicroseconds(value, readIntNode)); + @SuppressWarnings("unused") @Cached PyDeltaCheckNode checkNode) { + return new TimeDeltaValue(TimeDeltaNodes.FromNative.getDays(value), TimeDeltaNodes.FromNative.getSeconds(value), + TimeDeltaNodes.FromNative.getMicroseconds(value)); } @Fallback @@ -304,9 +303,8 @@ static DateValue doManaged(PDate value) { @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") static DateValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, - @SuppressWarnings("unused") @Cached PyDateCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readNode) { - return new DateValue(DateNodes.FromNative.getYear(value, readNode), DateNodes.FromNative.getMonth(value, readNode), DateNodes.FromNative.getDay(value, readNode)); + @SuppressWarnings("unused") @Cached PyDateCheckNode checkNode) { + return new DateValue(DateNodes.FromNative.getYear(value), DateNodes.FromNative.getMonth(value), DateNodes.FromNative.getDay(value)); } @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isDate(value)"}, limit = "1") @@ -346,11 +344,10 @@ static TimeValue doManaged(PTime value) { @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") static TimeValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, @SuppressWarnings("unused") @Cached PyTimeCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return new TimeValue(TimeNodes.FromNative.getHour(value, readByteNode), TimeNodes.FromNative.getMinute(value, readByteNode), - TimeNodes.FromNative.getSecond(value, readByteNode), TimeNodes.FromNative.getMicrosecond(value, readByteNode), - TimeNodes.FromNative.getTzInfo(value, readByteNode, readObjectNode), null, TimeNodes.FromNative.getFold(value, readByteNode)); + return new TimeValue(TimeNodes.FromNative.getHour(value), TimeNodes.FromNative.getMinute(value), + TimeNodes.FromNative.getSecond(value), TimeNodes.FromNative.getMicrosecond(value), + TimeNodes.FromNative.getTzInfo(value, readObjectNode), null, TimeNodes.FromNative.getFold(value)); } @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isTime(value)"}, limit = "1") @@ -391,13 +388,12 @@ static DateTimeValue doManaged(PDateTime value) { @Specialization(guards = "checkNode.execute(inliningTarget, value)", limit = "1") static DateTimeValue doNative(@SuppressWarnings("unused") Node inliningTarget, PythonAbstractNativeObject value, @SuppressWarnings("unused") @Cached PyDateTimeCheckNode checkNode, - @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return new DateTimeValue(DateTimeNodes.FromNative.getYear(value, readByteNode), DateTimeNodes.FromNative.getMonth(value, readByteNode), - DateTimeNodes.FromNative.getDay(value, readByteNode), DateTimeNodes.FromNative.getHour(value, readByteNode), - DateTimeNodes.FromNative.getMinute(value, readByteNode), DateTimeNodes.FromNative.getSecond(value, readByteNode), - DateTimeNodes.FromNative.getMicrosecond(value, readByteNode), DateTimeNodes.FromNative.getTzInfo(value, readByteNode, readObjectNode), null, - DateTimeNodes.FromNative.getFold(value, readByteNode)); + return new DateTimeValue(DateTimeNodes.FromNative.getYear(value), DateTimeNodes.FromNative.getMonth(value), + DateTimeNodes.FromNative.getDay(value), DateTimeNodes.FromNative.getHour(value), + DateTimeNodes.FromNative.getMinute(value), DateTimeNodes.FromNative.getSecond(value), + DateTimeNodes.FromNative.getMicrosecond(value), DateTimeNodes.FromNative.getTzInfo(value, readObjectNode), null, + DateTimeNodes.FromNative.getFold(value)); } @Specialization(guards = {"isForeignObjectNode.execute(inliningTarget, value)", "interop.isDate(value)", "interop.isTime(value)"}, limit = "1") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java index 78789de622..fcc8bf2859 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeBuiltins.java @@ -75,7 +75,6 @@ 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.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; @@ -88,9 +87,9 @@ import com.oracle.graal.python.lib.PyObjectHashNode; import com.oracle.graal.python.lib.PyObjectReprAsObjectNode; import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; -import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.lib.PyTZInfoCheckNode; import com.oracle.graal.python.lib.PyTimeCheckNode; +import com.oracle.graal.python.lib.PyUnicodeCheckNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; @@ -495,9 +494,8 @@ static int getHour(PTime self) { } @Specialization - static int getHour(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.FromNative.getHour(self, readNode); + static int getHour(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getHour(self); } @Specialization @@ -518,9 +516,8 @@ static int getMinute(PTime self) { } @Specialization - static int getMinute(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.FromNative.getMinute(self, readNode); + static int getMinute(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getMinute(self); } @Specialization @@ -541,9 +538,8 @@ static int getSecond(PTime self) { } @Specialization - static int getSecond(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.FromNative.getSecond(self, readNode); + static int getSecond(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getSecond(self); } @Specialization @@ -564,9 +560,8 @@ static int getMicrosecond(PTime self) { } @Specialization - static int getMicrosecond(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.FromNative.getMicrosecond(self, readNode); + static int getMicrosecond(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getMicrosecond(self); } @Specialization @@ -600,9 +595,8 @@ static int getFold(PTime self) { } @Specialization - static int getFold(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readNode) { - return TimeNodes.FromNative.getFold(self, readNode); + static int getFold(PythonAbstractNativeObject self) { + return TimeNodes.FromNative.getFold(self); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaBuiltins.java index 86bde99c63..7cf1b79071 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaBuiltins.java @@ -63,9 +63,9 @@ 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.modules.datetime.TemporalValueNodes.TimeDeltaValue; 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.structs.CStructAccess; 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.tuple.PTuple; @@ -76,9 +76,8 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotHashFun; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotRichCompare.RichCmpBuiltinNode; -import com.oracle.graal.python.builtins.modules.datetime.TemporalValueNodes.TimeDeltaValue; -import com.oracle.graal.python.lib.PyFloatCheckNode; import com.oracle.graal.python.lib.PyDeltaCheckNode; +import com.oracle.graal.python.lib.PyFloatCheckNode; import com.oracle.graal.python.lib.PyLongCheckNode; import com.oracle.graal.python.lib.PyNumberAddNode; import com.oracle.graal.python.lib.PyNumberFloorDivideNode; @@ -654,9 +653,8 @@ static int getDays(PTimeDelta self) { } @Specialization - static int getDays(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.FromNative.getDays(self, readNode); + static int getDays(PythonAbstractNativeObject self) { + return TimeDeltaNodes.FromNative.getDays(self); } } @@ -670,9 +668,8 @@ static int getSeconds(PTimeDelta self) { } @Specialization - static int getSeconds(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.FromNative.getSeconds(self, readNode); + static int getSeconds(PythonAbstractNativeObject self) { + return TimeDeltaNodes.FromNative.getSeconds(self); } } @@ -686,9 +683,8 @@ static int getMicroseconds(PTimeDelta self) { } @Specialization - static int getMicroseconds(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadI32Node readNode) { - return TimeDeltaNodes.FromNative.getMicroseconds(self, readNode); + static int getMicroseconds(PythonAbstractNativeObject self) { + return TimeDeltaNodes.FromNative.getMicroseconds(self); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java index 4ee7d60e23..086aab8826 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeDeltaNodes.java @@ -42,18 +42,22 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; +import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readIntField; +import java.lang.ref.Reference; import java.math.BigInteger; 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.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.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.capi.transitions.CApiTransitions.NativeToPythonTransferNode; 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.type.TypeNodes; import com.oracle.graal.python.lib.PyFloatAsDoubleNode; import com.oracle.graal.python.lib.PyFloatCheckNode; @@ -64,6 +68,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.util.CastToJavaBigIntegerNode; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; @@ -135,6 +140,8 @@ private static Object createTimeDelta(Node inliningTarget, Object cls, Shape sha return createTimeDeltaFromMicroseconds(inliningTarget, cls, shape, accumulator.getTotalMicroseconds()); } + private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_TIMEDELTA_SUBTYPE_NEW); + @TruffleBoundary private static Object createTimeDeltaFromMicroseconds(Node inliningTarget, Object cls, Shape shape, BigInteger microseconds) { BigInteger[] res = microseconds.divideAndRemainder(BIG_US_PER_SECOND); @@ -172,10 +179,17 @@ private static Object createTimeDeltaFromMicroseconds(Node inliningTarget, Objec secondsNormalized, microsecondsNormalized); } else { - Object nativeResult = CExtNodes.PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_TIMEDELTA_SUBTYPE_NEW, - CApiTransitions.PythonToNativeNode.executeUncached(cls), daysNormalized, secondsNormalized, microsecondsNormalized); - ExternalFunctionNodes.DefaultCheckFunctionResultNode.getUncached().execute(PythonContext.get(null), NativeCAPISymbol.FUN_TIMEDELTA_SUBTYPE_NEW.getTsName(), nativeResult); - return CApiTransitions.NativeToPythonTransferNode.executeUncached(nativeResult); + long clsPointer = CApiTransitions.PythonToNativeNode.executeLongUncached(cls); + try { + PythonContext context = PythonContext.get(null); + var callable = CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_TIMEDELTA_SUBTYPE_NEW); + long nativeResult = ExternalFunctionInvoker.invokeTIMEDELTA_SUBTYPE_NEW(null, C_API_TIMING, + context.ensureNativeContext(), BoundaryCallData.getUncached(), context.getThreadState(context.getLanguage(inliningTarget)), callable, + clsPointer, daysNormalized, secondsNormalized, microsecondsNormalized); + return PyObjectCheckFunctionResultNode.executeUncached(NativeCAPISymbol.FUN_TIMEDELTA_SUBTYPE_NEW.getTsName(), NativeToPythonTransferNode.executeRawUncached(nativeResult)); + } finally { + Reference.reachabilityFence(cls); + } } } @@ -236,16 +250,16 @@ public BigInteger getTotalMicroseconds() { } public static final class FromNative { - static int getDays(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { - return readNode.readFromObj(self, CFields.PyDateTime_Delta__days); + static int getDays(PythonAbstractNativeObject self) { + return readIntField(self.getPtr(), CFields.PyDateTime_Delta__days); } - static int getSeconds(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { - return readNode.readFromObj(self, CFields.PyDateTime_Delta__seconds); + static int getSeconds(PythonAbstractNativeObject self) { + return readIntField(self.getPtr(), CFields.PyDateTime_Delta__seconds); } - static int getMicroseconds(PythonAbstractNativeObject self, CStructAccess.ReadI32Node readNode) { - return readNode.readFromObj(self, CFields.PyDateTime_Delta__microseconds); + static int getMicroseconds(PythonAbstractNativeObject self) { + return readIntField(self.getPtr(), CFields.PyDateTime_Delta__microseconds); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java index c79fb86e0c..515084e153 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TimeNodes.java @@ -42,26 +42,34 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; +import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readByteField; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; +import java.lang.ref.Reference; + 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.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.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.capi.transitions.CApiTransitions.NativeToPythonTransferNode; 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.function.PKeyword; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetInstanceShape; -import com.oracle.graal.python.lib.PyTZInfoCheckNode; import com.oracle.graal.python.lib.PyLongAsIntNode; +import com.oracle.graal.python.lib.PyTZInfoCheckNode; +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; import com.oracle.graal.python.nodes.call.CallNode; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; @@ -144,6 +152,8 @@ static Object newTimeBoundary(Node inliningTarget, Object cls, Object hourObject return newTimeUnchecked(cls, hour, minute, second, microsecond, tzInfo, fold); } + private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_TIME_SUBTYPE_NEW); + @TruffleBoundary public static Object newTimeUnchecked(Object cls, int hour, int minute, int second, int microsecond, Object tzInfoObject, int fold) { final Object tzInfo; @@ -158,10 +168,20 @@ public static Object newTimeUnchecked(Object cls, int hour, int minute, int seco return new PTime(cls, shape, hour, minute, second, microsecond, tzInfo, fold); } else { CApiTransitions.PythonToNativeNode toNative = CApiTransitions.PythonToNativeNode.getUncached(); - Object nativeResult = CExtNodes.PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_TIME_SUBTYPE_NEW, - toNative.execute(cls), hour, minute, second, microsecond, toNative.execute(tzInfo != null ? tzInfo : PNone.NO_VALUE), fold); - ExternalFunctionNodes.DefaultCheckFunctionResultNode.getUncached().execute(PythonContext.get(null), NativeCAPISymbol.FUN_TIME_SUBTYPE_NEW.getTsName(), nativeResult); - return CApiTransitions.NativeToPythonTransferNode.executeUncached(nativeResult); + long clsPointer = toNative.executeLong(cls); + Object effectiveTzInfo = tzInfo != null ? tzInfo : PNone.NO_VALUE; + long tzInfoPointer = toNative.executeLong(effectiveTzInfo); + try { + PythonContext context = PythonContext.get(null); + var callable = CApiContext.getNativeSymbol(null, NativeCAPISymbol.FUN_TIME_SUBTYPE_NEW); + long nativeResult = ExternalFunctionInvoker.invokeTIME_SUBTYPE_NEW(null, C_API_TIMING, + context.ensureNativeContext(), BoundaryCallData.getUncached(), + context.getThreadState(context.getLanguage()), callable, clsPointer, hour, minute, second, microsecond, tzInfoPointer, fold); + return PyObjectCheckFunctionResultNode.executeUncached(NativeCAPISymbol.FUN_TIME_SUBTYPE_NEW.getTsName(), NativeToPythonTransferNode.executeRawUncached(nativeResult)); + } finally { + Reference.reachabilityFence(cls); + Reference.reachabilityFence(effectiveTzInfo); + } } } @@ -229,28 +249,32 @@ static boolean isBuiltinClass(Object cls) { } public static final class FromNative { - static int getHour(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 0); + static int getHour(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Time__data); + return NativeMemory.readByteArrayElement(ptr, 0) & 0xFF; } - static int getMinute(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 1); + static int getMinute(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Time__data); + return NativeMemory.readByteArrayElement(ptr, 1) & 0xFF; } - static int getSecond(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 2); + static int getSecond(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Time__data); + return NativeMemory.readByteArrayElement(ptr, 2) & 0xFF; } - static int getMicrosecond(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - int b3 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 3); - int b4 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 4); - int b5 = readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__data, 5); + static int getMicrosecond(PythonAbstractNativeObject self) { + long ptr = CStructAccess.getFieldPtr(self.getPtr(), CFields.PyDateTime_Time__data); + int b3 = NativeMemory.readByteArrayElement(ptr, 3) & 0xFF; + int b4 = NativeMemory.readByteArrayElement(ptr, 4) & 0xFF; + int b5 = NativeMemory.readByteArrayElement(ptr, 5) & 0xFF; return (b3 << 16) | (b4 << 8) | b5; } - static Object getTzInfo(PythonAbstractNativeObject nativeTime, CStructAccess.ReadByteNode readByteNode, CStructAccess.ReadObjectNode readObjectNode) { + static Object getTzInfo(PythonAbstractNativeObject nativeTime, CStructAccess.ReadObjectNode readObjectNode) { Object tzinfo = null; - if (readByteNode.readFromObj(nativeTime, CFields.PyDateTime_Time__hastzinfo) != 0) { + if (readByteField(nativeTime.getPtr(), CFields.PyDateTime_Time__hastzinfo) != 0) { Object tzinfoObj = readObjectNode.readFromObj(nativeTime, CFields.PyDateTime_Time__tzinfo); if (tzinfoObj != PNone.NO_VALUE) { tzinfo = tzinfoObj; @@ -259,8 +283,8 @@ static Object getTzInfo(PythonAbstractNativeObject nativeTime, CStructAccess.Rea return tzinfo; } - static int getFold(PythonAbstractNativeObject self, CStructAccess.ReadByteNode readNode) { - return readNode.readFromObjUnsigned(self, CFields.PyDateTime_Time__fold); + static int getFold(PythonAbstractNativeObject self) { + return readByteField(self.getPtr(), CFields.PyDateTime_Time__fold) & 0xFF; } } @@ -282,9 +306,8 @@ static Object getTzInfo(PTime self) { @Specialization static Object getTzInfo(PythonAbstractNativeObject self, - @Cached CStructAccess.ReadByteNode readByteNode, @Cached CStructAccess.ReadObjectNode readObjectNode) { - return FromNative.getTzInfo(self, readByteNode, readObjectNode); + return FromNative.getTzInfo(self, readObjectNode); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoBuiltins.java index b4cb72b416..b7c6b45dfc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/TzInfoBuiltins.java @@ -43,10 +43,12 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.NotImplementedError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; +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.SpecialMethodNames.T___GETINITARGS__; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; +import java.lang.ref.Reference; import java.util.List; import com.oracle.graal.python.PythonLanguage; @@ -58,10 +60,13 @@ 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.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.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.capi.transitions.CApiTransitions.NativeToPythonTransferNode; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; @@ -78,6 +83,7 @@ import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.dsl.Bind; @@ -102,6 +108,7 @@ protected List> getNodeFa @SlotSignature(name = "datetime.tzinfo", minNumOfPositionalArgs = 1, takesVarArgs = true, takesVarKeywordArgs = true) @GenerateNodeFactory public abstract static class NewNode extends PythonBuiltinNode { + private static final CApiTiming C_API_TIMING = CApiTiming.create(true, NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW); @Specialization static Object newTzInfo(Object cls, Object[] arguments, PKeyword[] keywords, @@ -111,11 +118,18 @@ static Object newTzInfo(Object cls, Object[] arguments, PKeyword[] keywords, if (!needsNativeAllocationNode.execute(inliningTarget, cls)) { return new PTzInfo(cls, getInstanceShape.execute(cls)); } else { - PythonContext context = PythonContext.get(null); CApiTransitions.PythonToNativeNode toNative = CApiTransitions.PythonToNativeNode.getUncached(); - Object nativeResult = CExtNodes.PCallCapiFunction.callUncached(NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW, toNative.execute(cls), context.getNativeNull(), context.getNativeNull()); - ExternalFunctionNodes.DefaultCheckFunctionResultNode.getUncached().execute(context, NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW.getTsName(), nativeResult); - return CApiTransitions.NativeToPythonTransferNode.executeUncached(nativeResult); + long clsPointer = toNative.executeLong(cls); + try { + PythonContext context = PythonContext.get(inliningTarget); + var callable = CApiContext.getNativeSymbol(inliningTarget, NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW); + long nativeResult = ExternalFunctionInvoker.invokePY_TYPE_GENERIC_NEW(null, C_API_TIMING, + context.ensureNativeContext(), BoundaryCallData.getUncached(), + context.getThreadState(context.getLanguage(inliningTarget)), callable, clsPointer, NULLPTR, NULLPTR); + return PyObjectCheckFunctionResultNode.executeUncached(NativeCAPISymbol.FUN_PY_TYPE_GENERIC_NEW.getTsName(), NativeToPythonTransferNode.executeRawUncached(nativeResult)); + } finally { + Reference.reachabilityFence(cls); + } } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java index 06c872c35f..b57df9ae59 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java @@ -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 @@ -9,6 +9,7 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.builtins.modules.json.JSONScannerBuiltins.RECURSION_LIMIT; 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.PGuards.isDouble; import static com.oracle.graal.python.nodes.PGuards.isInteger; import static com.oracle.graal.python.nodes.PGuards.isPFloat; @@ -32,7 +33,6 @@ import com.oracle.graal.python.builtins.modules.json.JSONEncoderBuiltinsClinicProviders.MakeEncoderClinicProviderGen; 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.HashingStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetIterator; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageIterator; @@ -443,7 +443,6 @@ static boolean appendSimpleObj(VirtualFrame frame, PJSONEncoder encoder, PJSONEn @Cached GetClassNode getClassNode, @Cached IsSubtypeNode isSubtypeNode, @Cached CastToTruffleStringNode.ReadNativeStringNode readNativeStringNode, - @Cached CStructAccess.ReadDoubleNode readNativeDoubleNode, @Cached CastToTruffleStringNode castToTruffleStringNode, @Cached StringNodes.StringMaterializeNode stringMaterializeNode, @Cached TruffleString.ByteIndexOfCodePointSetNode byteIndexOfCodePointSetNode1, @@ -514,7 +513,7 @@ static boolean appendSimpleObj(VirtualFrame frame, PJSONEncoder encoder, PJSONEn doubleValue = ((PFloat) obj).asDouble(); isDouble = true; } else if (obj instanceof PythonAbstractNativeObject nativeObj && isSubtypeNode.execute(getClassNode.execute(inliningTarget, nativeObj), PythonBuiltinClassType.PFloat)) { - doubleValue = readNativeDoubleNode.readFromObj(nativeObj, PyFloatObject__ob_fval); + doubleValue = readDoubleField(nativeObj.getPtr(), PyFloatObject__ob_fval); isDouble = true; } else { doubleValue = 0; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternNodes.java index 3550b9fff6..6b52d06a71 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/re/PatternNodes.java @@ -40,6 +40,11 @@ */ package com.oracle.graal.python.builtins.modules.re; +import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING_BINARY; +import static com.oracle.graal.python.util.PythonUtils.tsLiteral; + import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.modules.TRegexUtil; import com.oracle.graal.python.builtins.objects.PNone; @@ -47,7 +52,6 @@ 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.bytes.BytesNodes; -import com.oracle.graal.python.builtins.objects.cext.common.NativePointer; import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView; import com.oracle.graal.python.builtins.objects.mmap.PMMap; import com.oracle.graal.python.builtins.objects.module.PythonModule; @@ -83,11 +87,6 @@ import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING_BINARY; -import static com.oracle.graal.python.util.PythonUtils.tsLiteral; - public class PatternNodes { @GenerateInline @@ -421,13 +420,8 @@ static TruffleString convert(Node inliningTarget, Object buffer, } if (bufferLib.isNative(buffer)) { nativeProfile.enter(inliningTarget); - Object ptr = bufferLib.getNativePointer(buffer); - if (ptr != null) { - if (ptr instanceof Long lptr) { - ptr = new NativePointer(lptr); - } - return fromNativePointerNode.execute(ptr, 0, len, TS_ENCODING_BINARY, false); - } + long ptr = bufferLib.getNativePointer(buffer); + return fromNativePointerNode.execute(ptr, 0, len, TS_ENCODING_BINARY, false); } fallbackProfile.enter(inliningTarget); byte[] bytes = bufferLib.getCopiedByteArray(buffer); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressorBuiltins.java index 2a3fb8ff70..934112ba79 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressorBuiltins.java @@ -204,7 +204,7 @@ static PBytes decompress(VirtualFrame frame, ZlibDecompressorObject self, Object abstract static class DecompressBufInnerNode extends Node { abstract byte[] execute(VirtualFrame frame, Node inliningTarget, Object self, byte[] bytes, int length, int maxLength); - @Specialization(guards = {"self.isNative()"}) + @Specialization(guards = {"self.isNativeDecompressor()"}) static byte[] doNative(Node inliningTarget, ZlibDecompressorObject self, byte[] bytes, int length, int maxLength, @Cached ZlibNodes.ZlibNativeDecompressor decompress) { synchronized (self) { @@ -212,7 +212,7 @@ static byte[] doNative(Node inliningTarget, ZlibDecompressorObject self, byte[] } } - @Specialization(guards = {"!self.isNative()"}) + @Specialization(guards = {"!self.isNativeDecompressor()"}) static byte[] doJava(VirtualFrame frame, Node inliningTarget, ZlibDecompressorObject self, byte[] bytes, int length, int maxLength, @Cached(inline = false) BytesNodes.ToBytesNode toBytes) { byte[] ret = self.getStream().decompress(frame, bytes, length, maxLength, DEF_BUF_SIZE, inliningTarget, toBytes); @@ -231,7 +231,7 @@ static byte[] doJava(VirtualFrame frame, Node inliningTarget, ZlibDecompressorOb @Builtin(name = "unused_data", minNumOfPositionalArgs = 1, isGetter = true) @GenerateNodeFactory abstract static class UnusedDataNode extends PythonUnaryBuiltinNode { - @Specialization(guards = {"self.isInitialized()", "self.isNative()"}) + @Specialization(guards = {"self.isInitialized()", "self.isNativeDecompressor()"}) static PBytes doit(ZlibDecompressorObject self, @Bind Node inliningTarget, @Bind PythonContext context, @@ -242,12 +242,12 @@ static PBytes doit(ZlibDecompressorObject self, } } - @Specialization(guards = {"!self.isInitialized()", "self.isNative()"}) + @Specialization(guards = {"!self.isInitialized()", "self.isNativeDecompressor()"}) static PBytes doeof(ZlibDecompressorObject self) { return self.getUnusedData(); } - @Specialization(guards = "!self.isNative()") + @Specialization(guards = "!self.isNativeDecompressor()") static PBytes doit(ZlibDecompressorObject self) { return self.getUnusedData(); } @@ -256,12 +256,12 @@ static PBytes doit(ZlibDecompressorObject self) { @Builtin(name = "eof", minNumOfPositionalArgs = 1, isGetter = true) @GenerateNodeFactory abstract static class EOFNode extends PythonUnaryBuiltinNode { - @Specialization(guards = {"self.isEof() || !self.isInitialized()", "self.isNative()"}) + @Specialization(guards = {"self.isEof() || !self.isInitialized()", "self.isNativeDecompressor()"}) static boolean doit(ZlibDecompressorObject self) { return self.isEof(); } - @Specialization(guards = {"!self.isEof()", "self.isInitialized()", "self.isNative()"}) + @Specialization(guards = {"!self.isEof()", "self.isInitialized()", "self.isNativeDecompressor()"}) boolean doNative(ZlibDecompressorObject self, @Cached NativeLibrary.InvokeNativeFunction getEOF) { synchronized (self) { @@ -272,7 +272,7 @@ boolean doNative(ZlibDecompressorObject self, } } - @Specialization(guards = "!self.isNative()") + @Specialization(guards = "!self.isNativeDecompressor()") static boolean doJava(ZlibDecompressorObject self) { return self.isEof(); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressorObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressorObject.java index a1169ad90b..40179e5921 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressorObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/zlib/ZlibDecompressorObject.java @@ -86,12 +86,12 @@ public void setEof(boolean eof) { } public Object getZst() { - assert isNative(); + assert isNativeDecompressor(); return ((NativeZlibCompObject) compObject).getZst(); } public JavaDecompress getStream() { - assert !isNative(); + assert !isNativeDecompressor(); return ((JavaDecompress) compObject); } @@ -127,7 +127,7 @@ public void setUnconsumedTail(PBytes unconsumedTail) { compObject.setUnconsumedTail(unconsumedTail); } - public boolean isNative() { + public boolean isNativeDecompressor() { return compObject instanceof NativeZlibCompObject; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java index a9c736e00f..5a04dbef56 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java @@ -69,8 +69,6 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAccessLibrary; -import com.oracle.graal.python.builtins.objects.cext.capi.CApiGuards; -import com.oracle.graal.python.builtins.objects.cext.capi.PythonNativeWrapper.PythonAbstractObjectNativeWrapper; 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.HashingStorageGetIterator; @@ -191,10 +189,11 @@ @ImportStatic(SpecialMethodNames.class) @ExportLibrary(InteropLibrary.class) public abstract class PythonAbstractObject extends DynamicObject implements TruffleObject, Comparable { + public static final long UNINITIALIZED = -1; + public static final long NATIVE_POINTER_FREED = 0; private static final TruffleString T_PRIVATE_PREFIX = tsLiteral("__"); private static final int PRIVATE_PREFIX_LENGTH = T_PRIVATE_PREFIX.codePointLengthUncached(TS_ENCODING); - private PythonAbstractObjectNativeWrapper nativeWrapper; protected static final Shape ABSTRACT_SHAPE = Shape.newBuilder().build(); @@ -208,23 +207,6 @@ protected PythonAbstractObject() { super(ABSTRACT_SHAPE); } - public final PythonAbstractObjectNativeWrapper getNativeWrapper() { - return nativeWrapper; - } - - public final void setNativeWrapper(PythonAbstractObjectNativeWrapper nativeWrapper) { - assert this.nativeWrapper == null; - - // we must not set the native wrapper for one of the context-insensitive singletons - assert !CApiGuards.isSpecialSingleton(this); - - this.nativeWrapper = nativeWrapper; - } - - public final void clearNativeWrapper() { - nativeWrapper = null; - } - public Object[] getIndexedSlots() { return indexedSlots; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java index 8ffe4b2714..27d5fc3763 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java @@ -28,7 +28,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.EOFError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.IndexError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.MemoryError; -import static com.oracle.graal.python.builtins.PythonBuiltinClassType.NotImplementedError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.builtins.modules.io.IONodes.T_READ; @@ -153,8 +152,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.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.Node; @@ -972,17 +969,8 @@ abstract static class BufferInfoNode extends PythonUnaryBuiltinNode { static Object bufferinfo(PArray self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached ArrayNodes.EnsureNativeStorageNode ensureNativeStorageNode, - @CachedLibrary(limit = "1") InteropLibrary lib) { - Object nativePointer = ensureNativeStorageNode.execute(inliningTarget, self).getPtr(); - if (!(nativePointer instanceof Long)) { - try { - nativePointer = lib.asPointer(nativePointer); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw PRaiseNode.raiseStatic(inliningTarget, NotImplementedError); - } - } + @Cached ArrayNodes.EnsureNativeStorageNode ensureNativeStorageNode) { + long nativePointer = ensureNativeStorageNode.execute(inliningTarget, self).getPtr(); return PFactory.createTuple(language, new Object[]{nativePointer, self.getLength()}); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayNodes.java index f4570c743e..ce8da1e88c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayNodes.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 @@ -51,6 +51,7 @@ 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.Cached; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; @@ -100,10 +101,16 @@ static void check(VirtualFrame frame, Node inliningTarget, PArray array, Object } @GenerateInline + @GenerateUncached @GenerateCached(false) public abstract static class EnsureCapacityNode extends Node { public abstract void execute(Node inliningTarget, PArray array, int newCapacity); + @TruffleBoundary + public static void executeUncached(PArray array, int newCapacity) { + ArrayNodesFactory.EnsureCapacityNodeGen.getUncached().execute(null, array, newCapacity); + } + @Specialization static void ensure(Node inliningTarget, PArray array, int newCapacity, @Cached SequenceStorageNodes.EnsureCapacityNode ensureCapacityNode) { @@ -118,10 +125,16 @@ static void ensure(Node inliningTarget, PArray array, int newCapacity, } @GenerateInline + @GenerateUncached @GenerateCached(false) public abstract static class SetLengthNode extends Node { public abstract void execute(Node inliningTarget, PArray array, int newLength); + @TruffleBoundary + public static void executeUncached(PArray array, int newLength) { + ArrayNodesFactory.SetLengthNodeGen.getUncached().execute(null, array, newLength); + } + @Specialization static void set(Node inliningTarget, PArray array, int newLength, @Cached SequenceStorageNodes.SetLenNode setLenNode) { @@ -177,9 +190,15 @@ static void shift(Node inliningTarget, PArray array, int from, int by, @GenerateInline @GenerateCached(false) + @GenerateUncached public abstract static class EnsureNativeStorageNode extends Node { public abstract NativeByteSequenceStorage execute(Node inliningTarget, PArray array); + @TruffleBoundary + public static NativeByteSequenceStorage executeUncached(PArray array) { + return ArrayNodesFactory.EnsureNativeStorageNodeGen.getUncached().execute(null, array); + } + @Specialization static NativeByteSequenceStorage toNative(Node inliningTarget, PArray array, @Cached SequenceStorageNodes.StorageToNativeNode storageToNativeNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/PArray.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/PArray.java index da7d42398e..426ee78a95 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/PArray.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/PArray.java @@ -423,13 +423,13 @@ public boolean isArrayElementRemovable(long idx) { return isInBounds(idx); } - @ExportMessage - boolean isNative() { + @ExportMessage(library = PythonBufferAccessLibrary.class, name = "isNative") + boolean isNativeStorage() { return storage instanceof NativeByteSequenceStorage; } @ExportMessage - Object getNativePointer( + long getNativePointer( @Bind Node inliningTarget, @Cached PySequenceArrayWrapper.ToNativeStorageNode toNativeStorageNode) { NativeSequenceStorage newStorage = toNativeStorageNode.execute(inliningTarget, storage, true); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/PythonBufferAccessLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/PythonBufferAccessLibrary.java index a4ca397c1f..d74ab10fe0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/PythonBufferAccessLibrary.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/buffer/PythonBufferAccessLibrary.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,6 +40,7 @@ */ package com.oracle.graal.python.builtins.objects.buffer; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.util.BufferFormat.T_UINT_8_TYPE_CODE; import java.nio.ByteOrder; @@ -646,8 +647,8 @@ public boolean isNative(@SuppressWarnings("unused") Object receiver) { * interop pointer or a long. */ @Abstract(ifExported = "isNative") - public Object getNativePointer(@SuppressWarnings("unused") Object receiver) { - return null; + public long getNativePointer(@SuppressWarnings("unused") Object receiver) { + return NULLPTR; } static final LibraryFactory FACTORY = LibraryFactory.resolve(PythonBufferAccessLibrary.class); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesBuiltins.java index 5c6fb8c423..5ed4012f20 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. * Copyright (c) 2013, Regents of the University of California * * All rights reserved. @@ -32,14 +32,15 @@ import static com.oracle.graal.python.nodes.SpecialMethodNames.T___BYTES__; import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; +import java.lang.ref.Reference; import java.util.List; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.ArgumentClinic; +import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.annotations.Slot; import com.oracle.graal.python.annotations.Slot.SlotKind; import com.oracle.graal.python.annotations.Slot.SlotSignature; -import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.builtins.CoreFunctions; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; @@ -48,9 +49,11 @@ 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.bytes.BytesBuiltinsClinicProviders.BytesNewNodeClinicProviderGen; -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.AsCharPointerNode; +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.common.CArrayWrappers; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.SequenceStorageMpSubscriptNode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.SequenceStorageSqItemNode; @@ -65,6 +68,7 @@ import com.oracle.graal.python.lib.PyBytesCheckNode; import com.oracle.graal.python.lib.PyIndexCheckNode; import com.oracle.graal.python.lib.RichCmpOp; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; @@ -78,7 +82,9 @@ import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; 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.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; @@ -171,6 +177,8 @@ static Object dontCallBytes(VirtualFrame frame, Object cls, Object source, Objec @GenerateInline @GenerateCached(false) abstract static class CreateBytes extends PNodeWithContext { + private static final CApiTiming C_API_TIMING = CApiTiming.create(true, FUN_BYTES_SUBTYPE_NEW); + abstract Object execute(Node inliningTarget, Object cls, byte[] bytes); @Specialization(guards = "isBuiltinBytes(cls)") @@ -189,14 +197,20 @@ static PBytes doManaged(@SuppressWarnings("unused") Node inliningTarget, Object @Specialization(guards = "needsNativeAllocationNode.execute(inliningTarget, cls)") static Object doNative(@SuppressWarnings("unused") Node inliningTarget, Object cls, byte[] bytes, @SuppressWarnings("unused") @Shared @Cached TypeNodes.NeedsNativeAllocationNode needsNativeAllocationNode, + @Cached AsCharPointerNode asCharPointerNode, @Cached(inline = false) CApiTransitions.PythonToNativeNode toNative, - @Cached(inline = false) CApiTransitions.NativeToPythonTransferNode toPython, - @Cached(inline = false) CExtNodes.PCallCapiFunction call) { - CArrayWrappers.CByteArrayWrapper wrapper = new CArrayWrappers.CByteArrayWrapper(bytes); + @Cached(inline = false) CApiTransitions.NativeToPythonTransferNode toPython) { + long dataPointer = asCharPointerNode.execute(bytes); + long clsPointer = toNative.executeLong(cls); try { - return toPython.execute(call.call(FUN_BYTES_SUBTYPE_NEW, toNative.execute(cls), wrapper, bytes.length)); + PythonContext context = PythonContext.get(inliningTarget); + var callable = CApiContext.getNativeSymbol(inliningTarget, FUN_BYTES_SUBTYPE_NEW); + return toPython.execute(ExternalFunctionInvoker.invokeBYTES_SUBTYPE_NEW(null, C_API_TIMING, + context.ensureNativeContext(), BoundaryCallData.getUncached(), + context.getThreadState(context.getLanguage(inliningTarget)), callable, clsPointer, dataPointer, bytes.length)); } finally { - wrapper.free(); + Reference.reachabilityFence(cls); + NativeMemory.free(dataPointer); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java index f4f6cb56cf..21ac257983 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesCommonBuiltins.java @@ -245,8 +245,14 @@ static PBytesLike join(VirtualFrame frame, Object self, Object iterable, @Slot(value = SlotKind.sq_concat, isComplex = true) @GenerateNodeFactory + @GenerateUncached public abstract static class ConcatNode extends SqConcatBuiltinNode { + @TruffleBoundary + public static PBytesLike executeUncached(Object self, Object other) { + return (PBytesLike) BytesCommonBuiltinsFactory.ConcatNodeFactory.getInstance().getUncachedInstance().execute(null, self, other); + } + @Specialization static PBytesLike add(PBytesLike self, PBytesLike other, @Bind Node inliningTarget, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesNodes.java index 8daf58a4f1..e5b3ec6d62 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesNodes.java @@ -43,6 +43,8 @@ import static com.oracle.graal.python.builtins.objects.bytes.BytesUtils.isSpace; import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyBytesObject__ob_sval; 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.getFieldPtr; +import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField; import static com.oracle.graal.python.nodes.ErrorMessages.EXPECTED_BYTESLIKE_GOT_P; import static com.oracle.graal.python.nodes.ErrorMessages.NON_HEX_NUMBER_IN_FROMHEX; import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; @@ -70,7 +72,6 @@ import com.oracle.graal.python.builtins.objects.bytes.BytesNodesFactory.ToBytesNodeGen; import com.oracle.graal.python.builtins.objects.bytes.BytesNodesFactory.ToBytesWithoutFrameNodeGen; 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.GetInternalByteArrayNode; import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes; @@ -1003,12 +1004,11 @@ public abstract static class GetNativeBytesStorage extends Node { public abstract NativeByteSequenceStorage execute(PythonAbstractNativeObject tuple); @Specialization - NativeByteSequenceStorage getNative(PythonAbstractNativeObject bytes, - @Cached CStructAccess.GetElementPtrNode getContents, - @Cached CStructAccess.ReadI64Node readI64Node) { + NativeByteSequenceStorage getNative(PythonAbstractNativeObject bytes) { assert PyBytesCheckNode.executeUncached(bytes) || PyByteArrayCheckNode.executeUncached(bytes); - Object array = getContents.getElementPtr(bytes.getPtr(), PyBytesObject__ob_sval); - int size = (int) readI64Node.readFromObj(bytes, PyVarObject__ob_size); + long bytesRawPtr = bytes.getPtr(); + long array = getFieldPtr(bytesRawPtr, PyBytesObject__ob_sval); + int size = (int) readLongField(bytesRawPtr, PyVarObject__ob_size); return NativeByteSequenceStorage.create(array, size, size, false); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/PBytesLike.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/PBytesLike.java index d7f707a307..89a05af434 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/PBytesLike.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/PBytesLike.java @@ -158,13 +158,13 @@ float readFloatByteOrder(int byteOffset, ByteOrder byteOrder, return bufferLib.readDoubleByteOrder(store, byteOffset, byteOrder); } - @ExportMessage - boolean isNative() { + @ExportMessage(library = PythonBufferAccessLibrary.class, name = "isNative") + boolean isNativeStorage() { return store instanceof NativeByteSequenceStorage; } @ExportMessage - Object getNativePointer( + long getNativePointer( @Bind Node inliningTarget, @Cached PySequenceArrayWrapper.ToNativeStorageNode toNativeStorageNode) { NativeSequenceStorage newStorage = toNativeStorageNode.execute(inliningTarget, store, true); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/capsule/PyCapsule.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/capsule/PyCapsule.java index 95671f7bf7..7581172d41 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/capsule/PyCapsule.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/capsule/PyCapsule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, 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 @@ -41,13 +41,11 @@ package com.oracle.graal.python.builtins.objects.capsule; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.Capsule; - -import java.nio.charset.StandardCharsets; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodesFactory.FromCharPointerNodeGen; +import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.FromCharPointerNode; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions; -import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers; import com.oracle.graal.python.builtins.objects.object.PythonBuiltinObject; import com.oracle.graal.python.nodes.util.CastToJavaStringNode; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -58,31 +56,23 @@ @ExportLibrary(InteropLibrary.class) public final class PyCapsule extends PythonBuiltinObject { - public static byte[] capsuleName(String string) { - return string.getBytes(StandardCharsets.US_ASCII); - } - - public static boolean capsuleJavaNameIs(PyCapsule capsule, byte[] name) { - return capsule.getNamePtr() instanceof CArrayWrappers.CByteArrayWrapper wrapper && wrapper.getByteArray() == name; - } - /* * This class provides indirection to all the data members. Capsule destructors take the * capsule, so we use this to recreate a temporary "resurrected" capsule for the destructor * call. */ public static class CapsuleData { - private Object pointer; - private Object namePtr; - private Object context; - private Object destructor; + private long pointer; + private long namePtr; + private long context; + private long destructor; - public CapsuleData(Object pointer, Object namePtr) { + public CapsuleData(long pointer, long namePtr) { this.pointer = pointer; this.namePtr = namePtr; } - public Object getDestructor() { + public long getDestructor() { return destructor; } } @@ -104,37 +94,36 @@ public CapsuleData getData() { return data; } - public Object getPointer() { + public long getPointer() { return data.pointer; } - public void setPointer(Object pointer) { + public void setPointer(long pointer) { data.pointer = pointer; } - public Object getNamePtr() { + public long getNamePtr() { return data.namePtr; } - public void setNamePtr(Object name) { + public void setNamePtr(long name) { data.namePtr = name; } - public Object getContext() { + public long getContext() { return data.context; } - public void setContext(Object context) { + public void setContext(long context) { data.context = context; } - public Object getDestructor() { + public long getDestructor() { return data.destructor; } - public void registerDestructor(Object destructor) { - assert destructor == null || !InteropLibrary.getUncached().isNull(destructor); - if (reference == null && destructor != null) { + public void registerDestructor(long destructor) { + if (reference == null && destructor != NULLPTR) { reference = CApiTransitions.registerPyCapsuleDestructor(this); } data.destructor = destructor; @@ -144,9 +133,9 @@ public void registerDestructor(Object destructor) { @TruffleBoundary public String toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) { String quote, n; - if (data.namePtr != null) { + if (data.namePtr != NULLPTR) { quote = "\""; - n = CastToJavaStringNode.getUncached().execute(FromCharPointerNodeGen.getUncached().execute(data.namePtr, false)); + n = CastToJavaStringNode.getUncached().execute(FromCharPointerNode.executeUncached(data.namePtr)); } else { quote = ""; n = "NULL"; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/capsule/PyCapsuleNameMatchesNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/capsule/PyCapsuleNameMatchesNode.java deleted file mode 100644 index e96027cf5f..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/capsule/PyCapsuleNameMatchesNode.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2023, 2024, 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.capsule; - -import com.oracle.graal.python.builtins.objects.cext.common.CArrayWrappers; -import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.dsl.Cached; -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.interop.UnsupportedMessageException; -import com.oracle.truffle.api.library.CachedLibrary; -import com.oracle.truffle.api.nodes.Node; - -/** - * Compares two names according to the semantics of PyCapsule's {@code name_matches} function (see C - * code snippet below). The name objects can be native pointer objects, or {@code null}. - * - *
- *     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 rootNodeClass; + + public final ExternalFunctionSignature signature; + public final int numDefaults; + + PExternalFunctionWrapper(Class rootNodeClass, ExternalFunctionSignature signature) { + this(rootNodeClass, signature, 0); } - @TruffleBoundary - static RootCallTarget getOrCreateCallTarget(PExternalFunctionWrapper sig, PythonLanguage language, TruffleString name, boolean isStatic) { - Class 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 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: - * - *
-     *     if (func(self, args, kwds) < 0)
-     *         return NULL;
-     *     Py_RETURN_NONE;
-     * 
- * - * 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 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> 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> 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: + * + *
+     *     if (func(self, args, kwds) < 0)
+     *         return NULL;
+     *     Py_RETURN_NONE;
+     * 
+ * + * 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()]